[OStatus] adds status to pleroma instance if the url given is a status
[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 <- Repo.get(User, 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 case is_status?(acct) do
78 true ->
79 {:ok, object} = ActivityPub.fetch_object_from_id(acct)
80 %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
81 redirect(conn, to: "/notice/#{activity_id}")
82
83 false ->
84 {err, followee} = OStatus.find_or_make_user(acct)
85 avatar = User.avatar_url(followee)
86 name = followee.nickname
87 id = followee.id
88
89 if !!user do
90 conn
91 |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
92 else
93 conn
94 |> render("follow_login.html", %{
95 error: false,
96 acct: acct,
97 avatar: avatar,
98 name: name,
99 id: id
100 })
101 end
102 end
103 end
104
105 defp is_status?(acct) do
106 %URI{path: path} = URI.parse(acct)
107 Regex.match?(~r/\/users\/[^\/]+\/statuses\/([0-9]+)$/, path)
108 end
109
110 def do_remote_follow(conn, %{
111 "authorization" => %{"name" => username, "password" => password, "id" => id}
112 }) do
113 followee = Repo.get(User, id)
114 avatar = User.avatar_url(followee)
115 name = followee.nickname
116
117 with %User{} = user <- User.get_cached_by_nickname(username),
118 true <- Pbkdf2.checkpw(password, user.password_hash),
119 %User{} = _followed <- Repo.get(User, id),
120 {:ok, follower} <- User.follow(user, followee),
121 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
122 conn
123 |> render("followed.html", %{error: false})
124 else
125 # Was already following user
126 {:error, "Could not follow user:" <> _rest} ->
127 render(conn, "followed.html", %{error: false})
128
129 _e ->
130 conn
131 |> render("follow_login.html", %{
132 error: "Wrong username or password",
133 id: id,
134 name: name,
135 avatar: avatar
136 })
137 end
138 end
139
140 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
141 with %User{} = followee <- Repo.get(User, id),
142 {:ok, follower} <- User.follow(user, followee),
143 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
144 conn
145 |> render("followed.html", %{error: false})
146 else
147 # Was already following user
148 {:error, "Could not follow user:" <> _rest} ->
149 conn
150 |> render("followed.html", %{error: false})
151
152 e ->
153 Logger.debug("Remote follow failed with error #{inspect(e)}")
154
155 conn
156 |> render("followed.html", %{error: inspect(e)})
157 end
158 end
159
160 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
161 with {:ok, _} <- Notification.read_one(user, notification_id) do
162 json(conn, %{status: "success"})
163 else
164 {:error, message} ->
165 conn
166 |> put_resp_content_type("application/json")
167 |> send_resp(403, Jason.encode!(%{"error" => message}))
168 end
169 end
170
171 def config(conn, _params) do
172 instance = Pleroma.Config.get(:instance)
173 instance_fe = Pleroma.Config.get(:fe)
174 instance_chat = Pleroma.Config.get(:chat)
175
176 case get_format(conn) do
177 "xml" ->
178 response = """
179 <config>
180 <site>
181 <name>#{Keyword.get(instance, :name)}</name>
182 <site>#{Web.base_url()}</site>
183 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
184 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
185 </site>
186 </config>
187 """
188
189 conn
190 |> put_resp_content_type("application/xml")
191 |> send_resp(200, response)
192
193 _ ->
194 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
195
196 uploadlimit = %{
197 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
198 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
199 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
200 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
201 }
202
203 data = %{
204 name: Keyword.get(instance, :name),
205 description: Keyword.get(instance, :description),
206 server: Web.base_url(),
207 textlimit: to_string(Keyword.get(instance, :limit)),
208 uploadlimit: uploadlimit,
209 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
210 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
211 vapidPublicKey: vapid_public_key,
212 accountActivationRequired:
213 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
214 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
215 safeDMMentionsEnabled:
216 if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
217 }
218
219 pleroma_fe =
220 if instance_fe do
221 %{
222 theme: Keyword.get(instance_fe, :theme),
223 background: Keyword.get(instance_fe, :background),
224 logo: Keyword.get(instance_fe, :logo),
225 logoMask: Keyword.get(instance_fe, :logo_mask),
226 logoMargin: Keyword.get(instance_fe, :logo_margin),
227 redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
228 redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
229 chatDisabled: !Keyword.get(instance_chat, :enabled),
230 showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
231 scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
232 formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
233 collapseMessageWithSubject:
234 Keyword.get(instance_fe, :collapse_message_with_subject),
235 hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
236 hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
237 scopeCopy: Keyword.get(instance_fe, :scope_copy),
238 subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
239 alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
240 }
241 else
242 Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
243 end
244
245 managed_config = Keyword.get(instance, :managed_config)
246
247 data =
248 if managed_config do
249 data |> Map.put("pleromafe", pleroma_fe)
250 else
251 data
252 end
253
254 json(conn, %{site: data})
255 end
256 end
257
258 def frontend_configurations(conn, _params) do
259 config =
260 Pleroma.Config.get(:frontend_configurations, %{})
261 |> Enum.into(%{})
262
263 json(conn, config)
264 end
265
266 def version(conn, _params) do
267 version = Pleroma.Application.named_version()
268
269 case get_format(conn) do
270 "xml" ->
271 response = "<version>#{version}</version>"
272
273 conn
274 |> put_resp_content_type("application/xml")
275 |> send_resp(200, response)
276
277 _ ->
278 json(conn, version)
279 end
280 end
281
282 def emoji(conn, _params) do
283 json(conn, Enum.into(Emoji.get_all(), %{}))
284 end
285
286 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
287 follow_import(conn, %{"list" => File.read!(listfile.path)})
288 end
289
290 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
291 with followed_identifiers <- String.split(list),
292 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
293 json(conn, "job started")
294 end
295 end
296
297 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
298 blocks_import(conn, %{"list" => File.read!(listfile.path)})
299 end
300
301 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
302 with blocked_identifiers <- String.split(list),
303 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
304 json(conn, "job started")
305 end
306 end
307
308 def change_password(%{assigns: %{user: user}} = conn, params) do
309 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
310 {:ok, user} ->
311 with {:ok, _user} <-
312 User.reset_password(user, %{
313 password: params["new_password"],
314 password_confirmation: params["new_password_confirmation"]
315 }) do
316 json(conn, %{status: "success"})
317 else
318 {:error, changeset} ->
319 {_, {error, _}} = Enum.at(changeset.errors, 0)
320 json(conn, %{error: "New password #{error}."})
321
322 _ ->
323 json(conn, %{error: "Unable to change password."})
324 end
325
326 {:error, msg} ->
327 json(conn, %{error: msg})
328 end
329 end
330
331 def delete_account(%{assigns: %{user: user}} = conn, params) do
332 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
333 {:ok, user} ->
334 Task.start(fn -> User.delete(user) end)
335 json(conn, %{status: "success"})
336
337 {:error, msg} ->
338 json(conn, %{error: msg})
339 end
340 end
341
342 def captcha(conn, _params) do
343 json(conn, Pleroma.Captcha.new())
344 end
345 end