util_controller.ex 10.9 KB
Newer Older
1
# Pleroma: A lightweight social networking server
kaniini's avatar
kaniini committed
2
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

dtluna's avatar
dtluna committed
5
6
defmodule Pleroma.Web.TwitterAPI.UtilController do
  use Pleroma.Web, :controller
7

eal's avatar
eal committed
8
  require Logger
9

10
  alias Pleroma.Activity
Maksim's avatar
Maksim committed
11
  alias Pleroma.Config
Haelwenn's avatar
Haelwenn committed
12
  alias Pleroma.Emoji
Maksim's avatar
Maksim committed
13
  alias Pleroma.Healthcheck
14
  alias Pleroma.Notification
15
  alias Pleroma.Plugs.AuthenticationPlug
16
  alias Pleroma.User
17
  alias Pleroma.Web
Haelwenn's avatar
Haelwenn committed
18
19
  alias Pleroma.Web.CommonAPI
  alias Pleroma.Web.WebFinger
Roger Braun's avatar
Roger Braun committed
20

Maksim's avatar
Maksim committed
21
22
  plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])

dtluna's avatar
dtluna committed
23
24
25
26
  def help_test(conn, _params) do
    json(conn, "ok")
  end

eal's avatar
eal committed
27
  def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
Maksim's avatar
Maksim committed
28
29
    with %User{} = user <- User.get_cached_by_nickname(nick),
         avatar = User.avatar_url(user) do
eal's avatar
eal committed
30
31
32
      conn
      |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
    else
lain's avatar
lain committed
33
34
35
36
37
38
      _e ->
        render(conn, "subscribe.html", %{
          nickname: nick,
          avatar: nil,
          error: "Could not find user"
        })
eal's avatar
eal committed
39
40
    end
  end
lain's avatar
lain committed
41

eal's avatar
eal committed
42
43
44
45
46
47
48
  def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
    with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
         %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
      conn
      |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
    else
      _e ->
lain's avatar
lain committed
49
50
51
52
53
        render(conn, "subscribe.html", %{
          nickname: nick,
          avatar: nil,
          error: "Something went wrong."
        })
eal's avatar
eal committed
54
55
56
    end
  end

57
  def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
58
    if is_status?(acct) do
59
      {:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct)
60
61
      %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
      redirect(conn, to: "/notice/#{activity_id}")
62
    else
Maksim's avatar
Maksim committed
63
      with {:ok, followee} <- User.get_or_fetch(acct) do
64
        conn
Maksim's avatar
Maksim committed
65
        |> render(follow_template(user), %{
66
67
          error: false,
          acct: acct,
Maksim's avatar
Maksim committed
68
69
70
          avatar: User.avatar_url(followee),
          name: followee.nickname,
          id: followee.id
71
        })
Maksim's avatar
Maksim committed
72
73
74
      else
        {:error, _reason} ->
          render(conn, follow_template(user), %{error: :error})
75
      end
76
77
78
    end
  end

Maksim's avatar
Maksim committed
79
80
81
  defp follow_template(%User{} = _user), do: "follow.html"
  defp follow_template(_), do: "follow_login.html"

82
  defp is_status?(acct) do
83
    case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
84
85
86
87
88
      {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
        true

      _ ->
        false
89
90
91
    end
  end

lain's avatar
lain committed
92
93
94
  def do_remote_follow(conn, %{
        "authorization" => %{"name" => username, "password" => password, "id" => id}
      }) do
Maksim's avatar
Maksim committed
95
96
97
98
99
100
101
    with %User{} = followee <- User.get_cached_by_id(id),
         {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
         {_, true, _} <- {
           :auth,
           AuthenticationPlug.checkpw(password, user.password_hash),
           followee
         },
102
         {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
103
104
105
      conn
      |> render("followed.html", %{error: false})
    else
106
107
      # Was already following user
      {:error, "Could not follow user:" <> _rest} ->
Maksim's avatar
Maksim committed
108
        render(conn, "followed.html", %{error: "Error following account"})
109

Maksim's avatar
Maksim committed
110
      {:auth, _, followee} ->
111
        conn
lain's avatar
lain committed
112
113
114
        |> render("follow_login.html", %{
          error: "Wrong username or password",
          id: id,
Maksim's avatar
Maksim committed
115
116
          name: followee.nickname,
          avatar: User.avatar_url(followee)
lain's avatar
lain committed
117
        })
Maksim's avatar
Maksim committed
118
119
120
121

      e ->
        Logger.debug("Remote follow failed with error #{inspect(e)}")
        render(conn, "followed.html", %{error: "Something went wrong."})
122
123
    end
  end
lain's avatar
lain committed
124

125
  def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
Maksim's avatar
Maksim committed
126
    with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
127
         {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
128
129
130
      conn
      |> render("followed.html", %{error: false})
    else
131
132
      # Was already following user
      {:error, "Could not follow user:" <> _rest} ->
Maksim's avatar
Maksim committed
133
134
135
136
137
        render(conn, "followed.html", %{error: "Error following account"})

      {:fetch_user, error} ->
        Logger.debug("Remote follow failed with error #{inspect(error)}")
        render(conn, "followed.html", %{error: "Could not find user"})
138

139
      e ->
lain's avatar
lain committed
140
        Logger.debug("Remote follow failed with error #{inspect(e)}")
Maksim's avatar
Maksim committed
141
        render(conn, "followed.html", %{error: "Something went wrong."})
142
143
144
    end
  end

145
146
147
148
149
150
151
152
153
154
155
  def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
    with {:ok, _} <- Notification.read_one(user, notification_id) do
      json(conn, %{status: "success"})
    else
      {:error, message} ->
        conn
        |> put_resp_content_type("application/json")
        |> send_resp(403, Jason.encode!(%{"error" => message}))
    end
  end

Maksim's avatar
Maksim committed
156
  def config(%{assigns: %{format: "xml"}} = conn, _params) do
href's avatar
href committed
157
158
    instance = Pleroma.Config.get(:instance)

Maksim's avatar
Maksim committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    response = """
    <config>
    <site>
    <name>#{Keyword.get(instance, :name)}</name>
    <site>#{Web.base_url()}</site>
    <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
    <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
    </site>
    </config>
    """

    conn
    |> put_resp_content_type("application/xml")
    |> send_resp(200, response)
  end
lain's avatar
lain committed
174

Maksim's avatar
Maksim committed
175
176
  def config(conn, _params) do
    instance = Pleroma.Config.get(:instance)
177

Maksim's avatar
Maksim committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)

    uploadlimit = %{
      uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
      avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
      backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
      bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
    }

    data = %{
      name: Keyword.get(instance, :name),
      description: Keyword.get(instance, :description),
      server: Web.base_url(),
      textlimit: to_string(Keyword.get(instance, :limit)),
      uploadlimit: uploadlimit,
      closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
      private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
      vapidPublicKey: vapid_public_key,
      accountActivationRequired:
        bool_to_val(Keyword.get(instance, :account_activation_required, false)),
      invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
      safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
    }

    managed_config = Keyword.get(instance, :managed_config)

    data =
      if managed_config do
206
        pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
Maksim's avatar
Maksim committed
207
208
209
210
        Map.put(data, "pleromafe", pleroma_fe)
      else
        data
      end
211

Maksim's avatar
Maksim committed
212
    json(conn, %{site: data})
lain's avatar
lain committed
213
214
  end

Maksim's avatar
Maksim committed
215
216
217
218
219
  defp bool_to_val(true), do: "1"
  defp bool_to_val(_), do: "0"
  defp bool_to_val(true, val, _), do: val
  defp bool_to_val(_, _, val), do: val

lain's avatar
lain committed
220
221
222
223
224
225
226
227
  def frontend_configurations(conn, _params) do
    config =
      Pleroma.Config.get(:frontend_configurations, %{})
      |> Enum.into(%{})

    json(conn, config)
  end

Maksim's avatar
Maksim committed
228
  def version(%{assigns: %{format: "xml"}} = conn, _params) do
href's avatar
href committed
229
    version = Pleroma.Application.named_version()
lain's avatar
lain committed
230

Maksim's avatar
Maksim committed
231
232
233
234
    conn
    |> put_resp_content_type("application/xml")
    |> send_resp(200, "<version>#{version}</version>")
  end
lain's avatar
lain committed
235

Maksim's avatar
Maksim committed
236
237
  def version(conn, _params) do
    json(conn, Pleroma.Application.named_version())
dtluna's avatar
dtluna committed
238
  end
eal's avatar
eal committed
239
240

  def emoji(conn, _params) do
241
242
243
    emoji =
      Emoji.get_all()
      |> Enum.map(fn {short_code, path, tags} ->
244
        {short_code, %{image_url: path, tags: tags}}
245
      end)
246
      |> Enum.into(%{})
247
248

    json(conn, emoji)
eal's avatar
eal committed
249
  end
eal's avatar
eal committed
250

251
252
253
254
  def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
    with {:ok, _} <- User.update_notification_settings(user, params) do
      json(conn, %{status: "success"})
    end
eal's avatar
eal committed
255
  end
eal's avatar
eal committed
256

257
258
259
  def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
    follow_import(conn, %{"list" => File.read!(listfile.path)})
  end
lain's avatar
lain committed
260

261
  def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
262
263
264
265
266
    with lines <- String.split(list, "\n"),
         followed_identifiers <-
           Enum.map(lines, fn line ->
             String.split(line, ",") |> List.first()
           end)
267
268
269
270
271
272
273
           |> List.delete("Account address") do
      PleromaJobQueue.enqueue(:background, User, [
        :follow_import,
        follower,
        followed_identifiers
      ])

274
275
      json(conn, "job started")
    end
eal's avatar
eal committed
276
  end
277

278
279
280
281
  def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
    blocks_import(conn, %{"list" => File.read!(listfile.path)})
  end

282
  def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
283
284
285
286
287
288
289
    with blocked_identifiers <- String.split(list) do
      PleromaJobQueue.enqueue(:background, User, [
        :blocks_import,
        blocker,
        blocked_identifiers
      ])

290
291
      json(conn, "job started")
    end
292
293
  end

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  def change_password(%{assigns: %{user: user}} = conn, params) do
    case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
      {:ok, user} ->
        with {:ok, _user} <-
               User.reset_password(user, %{
                 password: params["new_password"],
                 password_confirmation: params["new_password_confirmation"]
               }) do
          json(conn, %{status: "success"})
        else
          {:error, changeset} ->
            {_, {error, _}} = Enum.at(changeset.errors, 0)
            json(conn, %{error: "New password #{error}."})

          _ ->
            json(conn, %{error: "Unable to change password."})
        end

      {:error, msg} ->
        json(conn, %{error: msg})
    end
  end

317
  def delete_account(%{assigns: %{user: user}} = conn, params) do
318
    case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
319
      {:ok, user} ->
320
        User.delete(user)
321
        json(conn, %{status: "success"})
322
323
324
325
326

      {:error, msg} ->
        json(conn, %{error: msg})
    end
  end
327

328
329
330
  def disable_account(%{assigns: %{user: user}} = conn, params) do
    case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
      {:ok, user} ->
331
        User.deactivate_async(user)
332
333
334
335
336
337
338
        json(conn, %{status: "success"})

      {:error, msg} ->
        json(conn, %{error: msg})
    end
  end

339
340
341
  def captcha(conn, _params) do
    json(conn, Pleroma.Captcha.new())
  end
342
343

  def healthcheck(conn, _params) do
Maksim's avatar
Maksim committed
344
345
346
347
348
349
    with true <- Config.get([:instance, :healthcheck]),
         %{healthy: true} = info <- Healthcheck.system_info() do
      json(conn, info)
    else
      %{healthy: false} = info ->
        service_unavailable(conn, info)
350

Maksim's avatar
Maksim committed
351
352
353
354
      _ ->
        service_unavailable(conn, %{})
    end
  end
355

Maksim's avatar
Maksim committed
356
357
358
359
  defp service_unavailable(conn, info) do
    conn
    |> put_status(:service_unavailable)
    |> json(info)
360
  end
dtluna's avatar
dtluna committed
361
end