Merge branch 'develop' into feature/hide-follows-remote
[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.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.WebFinger
21
22 def help_test(conn, _params) do
23 json(conn, "ok")
24 end
25
26 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
27 with %User{} = user <- User.get_cached_by_nickname(nick),
28 avatar = User.avatar_url(user) do
29 conn
30 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
31 else
32 _e ->
33 render(conn, "subscribe.html", %{
34 nickname: nick,
35 avatar: nil,
36 error: "Could not find user"
37 })
38 end
39 end
40
41 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
42 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
43 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
44 conn
45 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
46 else
47 _e ->
48 render(conn, "subscribe.html", %{
49 nickname: nick,
50 avatar: nil,
51 error: "Something went wrong."
52 })
53 end
54 end
55
56 def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
57 if is_status?(acct) do
58 {:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct)
59 %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
60 redirect(conn, to: "/notice/#{activity_id}")
61 else
62 {err, followee} = User.get_or_fetch(acct)
63 avatar = User.avatar_url(followee)
64 name = followee.nickname
65 id = followee.id
66
67 if !!user do
68 conn
69 |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
70 else
71 conn
72 |> render("follow_login.html", %{
73 error: false,
74 acct: acct,
75 avatar: avatar,
76 name: name,
77 id: id
78 })
79 end
80 end
81 end
82
83 defp is_status?(acct) do
84 case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
85 {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
86 true
87
88 _ ->
89 false
90 end
91 end
92
93 def do_remote_follow(conn, %{
94 "authorization" => %{"name" => username, "password" => password, "id" => id}
95 }) do
96 followee = User.get_cached_by_id(id)
97 avatar = User.avatar_url(followee)
98 name = followee.nickname
99
100 with %User{} = user <- User.get_cached_by_nickname(username),
101 true <- AuthenticationPlug.checkpw(password, user.password_hash),
102 %User{} = _followed <- User.get_cached_by_id(id),
103 {:ok, follower} <- User.follow(user, followee),
104 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
105 conn
106 |> render("followed.html", %{error: false})
107 else
108 # Was already following user
109 {:error, "Could not follow user:" <> _rest} ->
110 render(conn, "followed.html", %{error: false})
111
112 _e ->
113 conn
114 |> render("follow_login.html", %{
115 error: "Wrong username or password",
116 id: id,
117 name: name,
118 avatar: avatar
119 })
120 end
121 end
122
123 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
124 with %User{} = followee <- User.get_cached_by_id(id),
125 {:ok, follower} <- User.follow(user, followee),
126 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
127 conn
128 |> render("followed.html", %{error: false})
129 else
130 # Was already following user
131 {:error, "Could not follow user:" <> _rest} ->
132 conn
133 |> render("followed.html", %{error: false})
134
135 e ->
136 Logger.debug("Remote follow failed with error #{inspect(e)}")
137
138 conn
139 |> render("followed.html", %{error: inspect(e)})
140 end
141 end
142
143 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
144 with {:ok, _} <- Notification.read_one(user, notification_id) do
145 json(conn, %{status: "success"})
146 else
147 {:error, message} ->
148 conn
149 |> put_resp_content_type("application/json")
150 |> send_resp(403, Jason.encode!(%{"error" => message}))
151 end
152 end
153
154 def config(conn, _params) do
155 instance = Pleroma.Config.get(:instance)
156
157 case get_format(conn) do
158 "xml" ->
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
174 _ ->
175 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
176
177 uploadlimit = %{
178 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
179 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
180 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
181 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
182 }
183
184 data = %{
185 name: Keyword.get(instance, :name),
186 description: Keyword.get(instance, :description),
187 server: Web.base_url(),
188 textlimit: to_string(Keyword.get(instance, :limit)),
189 uploadlimit: uploadlimit,
190 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
191 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
192 vapidPublicKey: vapid_public_key,
193 accountActivationRequired:
194 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
195 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
196 safeDMMentionsEnabled:
197 if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
198 }
199
200 pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
201
202 managed_config = Keyword.get(instance, :managed_config)
203
204 data =
205 if managed_config do
206 data |> Map.put("pleromafe", pleroma_fe)
207 else
208 data
209 end
210
211 json(conn, %{site: data})
212 end
213 end
214
215 def frontend_configurations(conn, _params) do
216 config =
217 Pleroma.Config.get(:frontend_configurations, %{})
218 |> Enum.into(%{})
219
220 json(conn, config)
221 end
222
223 def version(conn, _params) do
224 version = Pleroma.Application.named_version()
225
226 case get_format(conn) do
227 "xml" ->
228 response = "<version>#{version}</version>"
229
230 conn
231 |> put_resp_content_type("application/xml")
232 |> send_resp(200, response)
233
234 _ ->
235 json(conn, version)
236 end
237 end
238
239 def emoji(conn, _params) do
240 emoji =
241 Emoji.get_all()
242 |> Enum.map(fn {short_code, path, tags} ->
243 {short_code, %{image_url: path, tags: tags}}
244 end)
245 |> Enum.into(%{})
246
247 json(conn, emoji)
248 end
249
250 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
251 with {:ok, _} <- User.update_notification_settings(user, params) do
252 json(conn, %{status: "success"})
253 end
254 end
255
256 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
257 follow_import(conn, %{"list" => File.read!(listfile.path)})
258 end
259
260 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
261 with lines <- String.split(list, "\n"),
262 followed_identifiers <-
263 Enum.map(lines, fn line ->
264 String.split(line, ",") |> List.first()
265 end)
266 |> List.delete("Account address") do
267 PleromaJobQueue.enqueue(:background, User, [
268 :follow_import,
269 follower,
270 followed_identifiers
271 ])
272
273 json(conn, "job started")
274 end
275 end
276
277 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
278 blocks_import(conn, %{"list" => File.read!(listfile.path)})
279 end
280
281 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
282 with blocked_identifiers <- String.split(list) do
283 PleromaJobQueue.enqueue(:background, User, [
284 :blocks_import,
285 blocker,
286 blocked_identifiers
287 ])
288
289 json(conn, "job started")
290 end
291 end
292
293 def change_password(%{assigns: %{user: user}} = conn, params) do
294 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
295 {:ok, user} ->
296 with {:ok, _user} <-
297 User.reset_password(user, %{
298 password: params["new_password"],
299 password_confirmation: params["new_password_confirmation"]
300 }) do
301 json(conn, %{status: "success"})
302 else
303 {:error, changeset} ->
304 {_, {error, _}} = Enum.at(changeset.errors, 0)
305 json(conn, %{error: "New password #{error}."})
306
307 _ ->
308 json(conn, %{error: "Unable to change password."})
309 end
310
311 {:error, msg} ->
312 json(conn, %{error: msg})
313 end
314 end
315
316 def delete_account(%{assigns: %{user: user}} = conn, params) do
317 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
318 {:ok, user} ->
319 User.delete(user)
320 json(conn, %{status: "success"})
321
322 {:error, msg} ->
323 json(conn, %{error: msg})
324 end
325 end
326
327 def disable_account(%{assigns: %{user: user}} = conn, params) do
328 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
329 {:ok, user} ->
330 User.deactivate_async(user)
331 json(conn, %{status: "success"})
332
333 {:error, msg} ->
334 json(conn, %{error: msg})
335 end
336 end
337
338 def captcha(conn, _params) do
339 json(conn, Pleroma.Captcha.new())
340 end
341
342 def healthcheck(conn, _params) do
343 with true <- Config.get([:instance, :healthcheck]),
344 %{healthy: true} = info <- Healthcheck.system_info() do
345 json(conn, info)
346 else
347 %{healthy: false} = info ->
348 service_unavailable(conn, info)
349
350 _ ->
351 service_unavailable(conn, %{})
352 end
353 end
354
355 defp service_unavailable(conn, info) do
356 conn
357 |> put_status(:service_unavailable)
358 |> json(info)
359 end
360 end