Merge branch 'hotfix/fix_undefined_summary' 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
292 json(conn, emoji)
293 end
294
295 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
296 with {:ok, _} <- User.update_notification_settings(user, params) do
297 json(conn, %{status: "success"})
298 end
299 end
300
301 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
302 follow_import(conn, %{"list" => File.read!(listfile.path)})
303 end
304
305 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
306 with followed_identifiers <- String.split(list),
307 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
308 json(conn, "job started")
309 end
310 end
311
312 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
313 blocks_import(conn, %{"list" => File.read!(listfile.path)})
314 end
315
316 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
317 with blocked_identifiers <- String.split(list),
318 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
319 json(conn, "job started")
320 end
321 end
322
323 def change_password(%{assigns: %{user: user}} = conn, params) do
324 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
325 {:ok, user} ->
326 with {:ok, _user} <-
327 User.reset_password(user, %{
328 password: params["new_password"],
329 password_confirmation: params["new_password_confirmation"]
330 }) do
331 json(conn, %{status: "success"})
332 else
333 {:error, changeset} ->
334 {_, {error, _}} = Enum.at(changeset.errors, 0)
335 json(conn, %{error: "New password #{error}."})
336
337 _ ->
338 json(conn, %{error: "Unable to change password."})
339 end
340
341 {:error, msg} ->
342 json(conn, %{error: msg})
343 end
344 end
345
346 def delete_account(%{assigns: %{user: user}} = conn, params) do
347 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
348 {:ok, user} ->
349 Task.start(fn -> User.delete(user) end)
350 json(conn, %{status: "success"})
351
352 {:error, msg} ->
353 json(conn, %{error: msg})
354 end
355 end
356
357 def captcha(conn, _params) do
358 json(conn, Pleroma.Captcha.new())
359 end
360 end