Merge branch 'feature/788-separate-email-addresses' 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_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} = ActivityPub.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 ActivityPub.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_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_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_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 instance_fe = Pleroma.Config.get(:fe)
177 instance_chat = Pleroma.Config.get(:chat)
178
179 case get_format(conn) do
180 "xml" ->
181 response = """
182 <config>
183 <site>
184 <name>#{Keyword.get(instance, :name)}</name>
185 <site>#{Web.base_url()}</site>
186 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
187 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
188 </site>
189 </config>
190 """
191
192 conn
193 |> put_resp_content_type("application/xml")
194 |> send_resp(200, response)
195
196 _ ->
197 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
198
199 uploadlimit = %{
200 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
201 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
202 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
203 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
204 }
205
206 data = %{
207 name: Keyword.get(instance, :name),
208 description: Keyword.get(instance, :description),
209 server: Web.base_url(),
210 textlimit: to_string(Keyword.get(instance, :limit)),
211 uploadlimit: uploadlimit,
212 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
213 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
214 vapidPublicKey: vapid_public_key,
215 accountActivationRequired:
216 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
217 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
218 safeDMMentionsEnabled:
219 if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
220 }
221
222 pleroma_fe =
223 if instance_fe do
224 %{
225 theme: Keyword.get(instance_fe, :theme),
226 background: Keyword.get(instance_fe, :background),
227 logo: Keyword.get(instance_fe, :logo),
228 logoMask: Keyword.get(instance_fe, :logo_mask),
229 logoMargin: Keyword.get(instance_fe, :logo_margin),
230 redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
231 redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
232 chatDisabled: !Keyword.get(instance_chat, :enabled),
233 showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
234 scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
235 formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
236 collapseMessageWithSubject:
237 Keyword.get(instance_fe, :collapse_message_with_subject),
238 hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
239 hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
240 scopeCopy: Keyword.get(instance_fe, :scope_copy),
241 subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
242 alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
243 }
244 else
245 Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
246 end
247
248 managed_config = Keyword.get(instance, :managed_config)
249
250 data =
251 if managed_config do
252 data |> Map.put("pleromafe", pleroma_fe)
253 else
254 data
255 end
256
257 json(conn, %{site: data})
258 end
259 end
260
261 def frontend_configurations(conn, _params) do
262 config =
263 Pleroma.Config.get(:frontend_configurations, %{})
264 |> Enum.into(%{})
265
266 json(conn, config)
267 end
268
269 def version(conn, _params) do
270 version = Pleroma.Application.named_version()
271
272 case get_format(conn) do
273 "xml" ->
274 response = "<version>#{version}</version>"
275
276 conn
277 |> put_resp_content_type("application/xml")
278 |> send_resp(200, response)
279
280 _ ->
281 json(conn, version)
282 end
283 end
284
285 def emoji(conn, _params) do
286 emoji =
287 Emoji.get_all()
288 |> Enum.map(fn {short_code, path, tags} ->
289 {short_code, %{image_url: path, tags: String.split(tags, ",")}}
290 end)
291 |> Enum.into(%{})
292
293 json(conn, emoji)
294 end
295
296 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
297 with {:ok, _} <- User.update_notification_settings(user, params) do
298 json(conn, %{status: "success"})
299 end
300 end
301
302 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
303 follow_import(conn, %{"list" => File.read!(listfile.path)})
304 end
305
306 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
307 with lines <- String.split(list, "\n"),
308 followed_identifiers <-
309 Enum.map(lines, fn line ->
310 String.split(line, ",") |> List.first()
311 end)
312 |> List.delete("Account address"),
313 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
314 json(conn, "job started")
315 end
316 end
317
318 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
319 blocks_import(conn, %{"list" => File.read!(listfile.path)})
320 end
321
322 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
323 with blocked_identifiers <- String.split(list),
324 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
325 json(conn, "job started")
326 end
327 end
328
329 def change_password(%{assigns: %{user: user}} = conn, params) do
330 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
331 {:ok, user} ->
332 with {:ok, _user} <-
333 User.reset_password(user, %{
334 password: params["new_password"],
335 password_confirmation: params["new_password_confirmation"]
336 }) do
337 json(conn, %{status: "success"})
338 else
339 {:error, changeset} ->
340 {_, {error, _}} = Enum.at(changeset.errors, 0)
341 json(conn, %{error: "New password #{error}."})
342
343 _ ->
344 json(conn, %{error: "Unable to change password."})
345 end
346
347 {:error, msg} ->
348 json(conn, %{error: msg})
349 end
350 end
351
352 def delete_account(%{assigns: %{user: user}} = conn, params) do
353 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
354 {:ok, user} ->
355 Task.start(fn -> User.delete(user) end)
356 json(conn, %{status: "success"})
357
358 {:error, msg} ->
359 json(conn, %{error: msg})
360 end
361 end
362
363 def captcha(conn, _params) do
364 json(conn, Pleroma.Captcha.new())
365 end
366 end