Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
[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.Emoji
12 alias Pleroma.PasswordResetToken
13 alias Pleroma.User
14 alias Pleroma.Repo
15 alias Pleroma.Web
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.OStatus
18 alias Pleroma.Web.WebFinger
19 alias Pleroma.Web.ActivityPub.ActivityPub
20
21 def show_password_reset(conn, %{"token" => token}) do
22 with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
23 %User{} = user <- Repo.get(User, token.user_id) do
24 render(conn, "password_reset.html", %{
25 token: token,
26 user: user
27 })
28 else
29 _e -> render(conn, "invalid_token.html")
30 end
31 end
32
33 def password_reset(conn, %{"data" => data}) do
34 with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
35 render(conn, "password_reset_success.html")
36 else
37 _e -> render(conn, "password_reset_failed.html")
38 end
39 end
40
41 def help_test(conn, _params) do
42 json(conn, "ok")
43 end
44
45 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
46 with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do
47 conn
48 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
49 else
50 _e ->
51 render(conn, "subscribe.html", %{
52 nickname: nick,
53 avatar: nil,
54 error: "Could not find user"
55 })
56 end
57 end
58
59 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
60 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
61 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
62 conn
63 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
64 else
65 _e ->
66 render(conn, "subscribe.html", %{
67 nickname: nick,
68 avatar: nil,
69 error: "Something went wrong."
70 })
71 end
72 end
73
74 def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
75 {err, followee} = OStatus.find_or_make_user(acct)
76 avatar = User.avatar_url(followee)
77 name = followee.nickname
78 id = followee.id
79
80 if !!user do
81 conn
82 |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
83 else
84 conn
85 |> render("follow_login.html", %{
86 error: false,
87 acct: acct,
88 avatar: avatar,
89 name: name,
90 id: id
91 })
92 end
93 end
94
95 def do_remote_follow(conn, %{
96 "authorization" => %{"name" => username, "password" => password, "id" => id}
97 }) do
98 followee = Repo.get(User, id)
99 avatar = User.avatar_url(followee)
100 name = followee.nickname
101
102 with %User{} = user <- User.get_cached_by_nickname(username),
103 true <- Pbkdf2.checkpw(password, user.password_hash),
104 %User{} = _followed <- Repo.get(User, id),
105 {:ok, follower} <- User.follow(user, followee),
106 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
107 conn
108 |> render("followed.html", %{error: false})
109 else
110 # Was already following user
111 {:error, "Could not follow user:" <> _rest} ->
112 render(conn, "followed.html", %{error: false})
113
114 _e ->
115 conn
116 |> render("follow_login.html", %{
117 error: "Wrong username or password",
118 id: id,
119 name: name,
120 avatar: avatar
121 })
122 end
123 end
124
125 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
126 with %User{} = followee <- Repo.get(User, id),
127 {:ok, follower} <- User.follow(user, followee),
128 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
129 conn
130 |> render("followed.html", %{error: false})
131 else
132 # Was already following user
133 {:error, "Could not follow user:" <> _rest} ->
134 conn
135 |> render("followed.html", %{error: false})
136
137 e ->
138 Logger.debug("Remote follow failed with error #{inspect(e)}")
139
140 conn
141 |> render("followed.html", %{error: inspect(e)})
142 end
143 end
144
145 def config(conn, _params) do
146 instance = Pleroma.Config.get(:instance)
147 instance_fe = Pleroma.Config.get(:fe)
148 instance_chat = Pleroma.Config.get(:chat)
149
150 case get_format(conn) do
151 "xml" ->
152 response = """
153 <config>
154 <site>
155 <name>#{Keyword.get(instance, :name)}</name>
156 <site>#{Web.base_url()}</site>
157 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
158 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
159 </site>
160 </config>
161 """
162
163 conn
164 |> put_resp_content_type("application/xml")
165 |> send_resp(200, response)
166
167 _ ->
168 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
169
170 uploadlimit = %{
171 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
172 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
173 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
174 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
175 }
176
177 data = %{
178 name: Keyword.get(instance, :name),
179 description: Keyword.get(instance, :description),
180 server: Web.base_url(),
181 textlimit: to_string(Keyword.get(instance, :limit)),
182 uploadlimit: uploadlimit,
183 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
184 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
185 vapidPublicKey: vapid_public_key,
186 accountActivationRequired:
187 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
188 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0")
189 }
190
191 pleroma_fe =
192 if instance_fe do
193 %{
194 theme: Keyword.get(instance_fe, :theme),
195 background: Keyword.get(instance_fe, :background),
196 logo: Keyword.get(instance_fe, :logo),
197 logoMask: Keyword.get(instance_fe, :logo_mask),
198 logoMargin: Keyword.get(instance_fe, :logo_margin),
199 redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
200 redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
201 chatDisabled: !Keyword.get(instance_chat, :enabled),
202 showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
203 scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
204 formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
205 collapseMessageWithSubject:
206 Keyword.get(instance_fe, :collapse_message_with_subject),
207 hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
208 hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
209 scopeCopy: Keyword.get(instance_fe, :scope_copy),
210 subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
211 alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
212 }
213 else
214 Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
215 end
216
217 managed_config = Keyword.get(instance, :managed_config)
218
219 data =
220 if managed_config do
221 data |> Map.put("pleromafe", pleroma_fe)
222 else
223 data
224 end
225
226 json(conn, %{site: data})
227 end
228 end
229
230 def frontend_configurations(conn, _params) do
231 config =
232 Pleroma.Config.get(:frontend_configurations, %{})
233 |> Enum.into(%{})
234
235 json(conn, config)
236 end
237
238 def version(conn, _params) do
239 version = Pleroma.Application.named_version()
240
241 case get_format(conn) do
242 "xml" ->
243 response = "<version>#{version}</version>"
244
245 conn
246 |> put_resp_content_type("application/xml")
247 |> send_resp(200, response)
248
249 _ ->
250 json(conn, version)
251 end
252 end
253
254 def emoji(conn, _params) do
255 json(conn, Enum.into(Emoji.get_all(), %{}))
256 end
257
258 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
259 follow_import(conn, %{"list" => File.read!(listfile.path)})
260 end
261
262 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
263 with followed_identifiers <- String.split(list),
264 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
265 json(conn, "job started")
266 end
267 end
268
269 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
270 blocks_import(conn, %{"list" => File.read!(listfile.path)})
271 end
272
273 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
274 with blocked_identifiers <- String.split(list),
275 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
276 json(conn, "job started")
277 end
278 end
279
280 def change_password(%{assigns: %{user: user}} = conn, params) do
281 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
282 {:ok, user} ->
283 with {:ok, _user} <-
284 User.reset_password(user, %{
285 password: params["new_password"],
286 password_confirmation: params["new_password_confirmation"]
287 }) do
288 json(conn, %{status: "success"})
289 else
290 {:error, changeset} ->
291 {_, {error, _}} = Enum.at(changeset.errors, 0)
292 json(conn, %{error: "New password #{error}."})
293
294 _ ->
295 json(conn, %{error: "Unable to change password."})
296 end
297
298 {:error, msg} ->
299 json(conn, %{error: msg})
300 end
301 end
302
303 def delete_account(%{assigns: %{user: user}} = conn, params) do
304 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
305 {:ok, user} ->
306 Task.start(fn -> User.delete(user) end)
307 json(conn, %{status: "success"})
308
309 {:error, msg} ->
310 json(conn, %{error: msg})
311 end
312 end
313
314 def disable_account(%{assigns: %{user: user}} = conn, params) do
315 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
316 {:ok, user} ->
317 User.disable_async(user)
318 json(conn, %{status: "success"})
319
320 {:error, msg} ->
321 json(conn, %{error: msg})
322 end
323 end
324
325 def captcha(conn, _params) do
326 json(conn, Pleroma.Captcha.new())
327 end
328 end