First
[anni] / lib / pleroma / user.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.User do
6   use Ecto.Schema
7
8   import Ecto.Changeset
9   import Ecto.Query
10   import Ecto, only: [assoc: 2]
11
12   alias Ecto.Multi
13   alias Pleroma.Activity
14   alias Pleroma.Config
15   alias Pleroma.Conversation.Participation
16   alias Pleroma.Delivery
17   alias Pleroma.EctoType.ActivityPub.ObjectValidators
18   alias Pleroma.Emoji
19   alias Pleroma.FollowingRelationship
20   alias Pleroma.Formatter
21   alias Pleroma.HTML
22   alias Pleroma.Keys
23   alias Pleroma.MFA
24   alias Pleroma.Notification
25   alias Pleroma.Object
26   alias Pleroma.Registration
27   alias Pleroma.Repo
28   alias Pleroma.User
29   alias Pleroma.UserRelationship
30   alias Pleroma.Web.ActivityPub.ActivityPub
31   alias Pleroma.Web.ActivityPub.Builder
32   alias Pleroma.Web.ActivityPub.Pipeline
33   alias Pleroma.Web.ActivityPub.Utils
34   alias Pleroma.Web.CommonAPI
35   alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
36   alias Pleroma.Web.Endpoint
37   alias Pleroma.Web.OAuth
38   alias Pleroma.Web.RelMe
39   alias Pleroma.Workers.BackgroundWorker
40
41   require Logger
42
43   @type t :: %__MODULE__{}
44   @type account_status ::
45           :active
46           | :deactivated
47           | :password_reset_pending
48           | :confirmation_pending
49           | :approval_pending
50   @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
51
52   # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
53   @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
54
55   @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
56   @extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/
57
58   # AP ID user relationships (blocks, mutes etc.)
59   # Format: [rel_type: [outgoing_rel: :outgoing_rel_target, incoming_rel: :incoming_rel_source]]
60   @user_relationships_config [
61     block: [
62       blocker_blocks: :blocked_users,
63       blockee_blocks: :blocker_users
64     ],
65     mute: [
66       muter_mutes: :muted_users,
67       mutee_mutes: :muter_users
68     ],
69     reblog_mute: [
70       reblog_muter_mutes: :reblog_muted_users,
71       reblog_mutee_mutes: :reblog_muter_users
72     ],
73     notification_mute: [
74       notification_muter_mutes: :notification_muted_users,
75       notification_mutee_mutes: :notification_muter_users
76     ],
77     # Note: `inverse_subscription` relationship is inverse: subscriber acts as relationship target
78     inverse_subscription: [
79       subscribee_subscriptions: :subscriber_users,
80       subscriber_subscriptions: :subscribee_users
81     ],
82     endorsement: [
83       endorser_endorsements: :endorsed_users,
84       endorsee_endorsements: :endorser_users
85     ]
86   ]
87
88   @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
89
90   schema "users" do
91     field(:bio, :string, default: "")
92     field(:raw_bio, :string)
93     field(:email, :string)
94     field(:name, :string)
95     field(:nickname, :string)
96     field(:password_hash, :string)
97     field(:password, :string, virtual: true)
98     field(:password_confirmation, :string, virtual: true)
99     field(:keys, :string)
100     field(:public_key, :string)
101     field(:ap_id, :string)
102     field(:avatar, :map, default: %{})
103     field(:local, :boolean, default: true)
104     field(:follower_address, :string)
105     field(:following_address, :string)
106     field(:featured_address, :string)
107     field(:search_rank, :float, virtual: true)
108     field(:search_type, :integer, virtual: true)
109     field(:tags, {:array, :string}, default: [])
110     field(:last_refreshed_at, :naive_datetime_usec)
111     field(:last_digest_emailed_at, :naive_datetime)
112     field(:banner, :map, default: %{})
113     field(:background, :map, default: %{})
114     field(:note_count, :integer, default: 0)
115     field(:follower_count, :integer, default: 0)
116     field(:following_count, :integer, default: 0)
117     field(:is_locked, :boolean, default: false)
118     field(:is_confirmed, :boolean, default: true)
119     field(:password_reset_pending, :boolean, default: false)
120     field(:is_approved, :boolean, default: true)
121     field(:registration_reason, :string, default: nil)
122     field(:confirmation_token, :string, default: nil)
123     field(:default_scope, :string, default: "public")
124     field(:domain_blocks, {:array, :string}, default: [])
125     field(:is_active, :boolean, default: true)
126     field(:no_rich_text, :boolean, default: false)
127     field(:ap_enabled, :boolean, default: false)
128     field(:is_moderator, :boolean, default: false)
129     field(:is_admin, :boolean, default: false)
130     field(:show_role, :boolean, default: true)
131     field(:uri, ObjectValidators.Uri, default: nil)
132     field(:hide_followers_count, :boolean, default: false)
133     field(:hide_follows_count, :boolean, default: false)
134     field(:hide_followers, :boolean, default: false)
135     field(:hide_follows, :boolean, default: false)
136     field(:hide_favorites, :boolean, default: true)
137     field(:email_notifications, :map, default: %{"digest" => false})
138     field(:mascot, :map, default: nil)
139     field(:emoji, :map, default: %{})
140     field(:pleroma_settings_store, :map, default: %{})
141     field(:fields, {:array, :map}, default: [])
142     field(:raw_fields, {:array, :map}, default: [])
143     field(:is_discoverable, :boolean, default: false)
144     field(:invisible, :boolean, default: false)
145     field(:allow_following_move, :boolean, default: true)
146     field(:skip_thread_containment, :boolean, default: false)
147     field(:actor_type, :string, default: "Person")
148     field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
149     field(:inbox, :string)
150     field(:shared_inbox, :string)
151     field(:accepts_chat_messages, :boolean, default: nil)
152     field(:last_active_at, :naive_datetime)
153     field(:disclose_client, :boolean, default: true)
154     field(:pinned_objects, :map, default: %{})
155     field(:is_suggested, :boolean, default: false)
156     field(:last_status_at, :naive_datetime)
157     field(:birthday, :date)
158     field(:show_birthday, :boolean, default: false)
159     field(:language, :string)
160
161     embeds_one(
162       :notification_settings,
163       Pleroma.User.NotificationSetting,
164       on_replace: :update
165     )
166
167     has_many(:notifications, Notification)
168     has_many(:registrations, Registration)
169     has_many(:deliveries, Delivery)
170
171     has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
172     has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
173
174     for {relationship_type,
175          [
176            {outgoing_relation, outgoing_relation_target},
177            {incoming_relation, incoming_relation_source}
178          ]} <- @user_relationships_config do
179       # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
180       #   :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
181       has_many(outgoing_relation, UserRelationship,
182         foreign_key: :source_id,
183         where: [relationship_type: relationship_type]
184       )
185
186       # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
187       #   :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
188       has_many(incoming_relation, UserRelationship,
189         foreign_key: :target_id,
190         where: [relationship_type: relationship_type]
191       )
192
193       # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
194       #   :notification_muted_users, :subscriber_users, :endorsed_users
195       has_many(outgoing_relation_target, through: [outgoing_relation, :target])
196
197       # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
198       #   :notification_muter_users, :subscribee_users, :endorser_users
199       has_many(incoming_relation_source, through: [incoming_relation, :source])
200     end
201
202     # `:blocks` is deprecated (replaced with `blocked_users` relation)
203     field(:blocks, {:array, :string}, default: [])
204     # `:mutes` is deprecated (replaced with `muted_users` relation)
205     field(:mutes, {:array, :string}, default: [])
206     # `:muted_reblogs` is deprecated (replaced with `reblog_muted_users` relation)
207     field(:muted_reblogs, {:array, :string}, default: [])
208     # `:muted_notifications` is deprecated (replaced with `notification_muted_users` relation)
209     field(:muted_notifications, {:array, :string}, default: [])
210     # `:subscribers` is deprecated (replaced with `subscriber_users` relation)
211     field(:subscribers, {:array, :string}, default: [])
212
213     embeds_one(
214       :multi_factor_authentication_settings,
215       MFA.Settings,
216       on_replace: :delete
217     )
218
219     timestamps()
220   end
221
222   for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
223         @user_relationships_config do
224     # `def blocked_users_relation/2`, `def muted_users_relation/2`,
225     #   `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
226     #   `def subscriber_users/2`, `def endorsed_users_relation/2`
227     def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
228       target_users_query = assoc(user, unquote(outgoing_relation_target))
229
230       if restrict_deactivated? do
231         target_users_query
232         |> User.Query.build(%{deactivated: false})
233       else
234         target_users_query
235       end
236     end
237
238     # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
239     #   `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
240     def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
241       __MODULE__
242       |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
243         user,
244         restrict_deactivated?
245       ])
246       |> Repo.all()
247     end
248
249     # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
250     #   `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
251     #   `def endorsed_users_ap_ids/2`
252     def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
253       __MODULE__
254       |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
255         user,
256         restrict_deactivated?
257       ])
258       |> select([u], u.ap_id)
259       |> Repo.all()
260     end
261   end
262
263   def cached_blocked_users_ap_ids(user) do
264     @cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ ->
265       blocked_users_ap_ids(user)
266     end)
267   end
268
269   def cached_muted_users_ap_ids(user) do
270     @cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ ->
271       muted_users_ap_ids(user)
272     end)
273   end
274
275   defdelegate following_count(user), to: FollowingRelationship
276   defdelegate following(user), to: FollowingRelationship
277   defdelegate following?(follower, followed), to: FollowingRelationship
278   defdelegate following_ap_ids(user), to: FollowingRelationship
279   defdelegate get_follow_requests(user), to: FollowingRelationship
280   defdelegate search(query, opts \\ []), to: User.Search
281
282   @doc """
283   Dumps Flake Id to SQL-compatible format (16-byte UUID).
284   E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
285   """
286   def binary_id(source_id) when is_binary(source_id) do
287     with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do
288       dumped_id
289     else
290       _ -> source_id
291     end
292   end
293
294   def binary_id(source_ids) when is_list(source_ids) do
295     Enum.map(source_ids, &binary_id/1)
296   end
297
298   def binary_id(%User{} = user), do: binary_id(user.id)
299
300   @doc "Returns status account"
301   @spec account_status(User.t()) :: account_status()
302   def account_status(%User{is_active: false}), do: :deactivated
303   def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
304   def account_status(%User{local: true, is_approved: false}), do: :approval_pending
305   def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending
306   def account_status(%User{}), do: :active
307
308   @spec visible_for(User.t(), User.t() | nil) ::
309           :visible
310           | :invisible
311           | :restricted_unauthenticated
312           | :deactivated
313           | :confirmation_pending
314   def visible_for(user, for_user \\ nil)
315
316   def visible_for(%User{invisible: true}, _), do: :invisible
317
318   def visible_for(%User{id: user_id}, %User{id: user_id}), do: :visible
319
320   def visible_for(%User{} = user, nil) do
321     if restrict_unauthenticated?(user) do
322       :restrict_unauthenticated
323     else
324       visible_account_status(user)
325     end
326   end
327
328   def visible_for(%User{} = user, for_user) do
329     if privileged?(for_user, :users_manage_activation_state) do
330       :visible
331     else
332       visible_account_status(user)
333     end
334   end
335
336   def visible_for(_, _), do: :invisible
337
338   defp restrict_unauthenticated?(%User{local: true}) do
339     Config.restrict_unauthenticated_access?(:profiles, :local)
340   end
341
342   defp restrict_unauthenticated?(%User{local: _}) do
343     Config.restrict_unauthenticated_access?(:profiles, :remote)
344   end
345
346   defp visible_account_status(user) do
347     status = account_status(user)
348
349     if status in [:active, :password_reset_pending] do
350       :visible
351     else
352       status
353     end
354   end
355
356   @spec privileged?(User.t(), atom()) :: boolean()
357   def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false
358
359   def privileged?(
360         %User{local: true, is_admin: is_admin, is_moderator: is_moderator},
361         privilege_tag
362       ),
363       do:
364         privileged_for?(privilege_tag, is_admin, :admin_privileges) or
365           privileged_for?(privilege_tag, is_moderator, :moderator_privileges)
366
367   def privileged?(_, _), do: false
368
369   defp privileged_for?(privilege_tag, true, config_role_key),
370     do: privilege_tag in Config.get([:instance, config_role_key])
371
372   defp privileged_for?(_, _, _), do: false
373
374   @spec privileges(User.t()) :: [atom()]
375   def privileges(%User{local: false}) do
376     []
377   end
378
379   def privileges(%User{is_moderator: false, is_admin: false}) do
380     []
381   end
382
383   def privileges(%User{local: true, is_moderator: true, is_admin: true}) do
384     (Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges]))
385     |> Enum.uniq()
386   end
387
388   def privileges(%User{local: true, is_moderator: true, is_admin: false}) do
389     Config.get([:instance, :moderator_privileges])
390   end
391
392   def privileges(%User{local: true, is_moderator: false, is_admin: true}) do
393     Config.get([:instance, :admin_privileges])
394   end
395
396   @spec invisible?(User.t()) :: boolean()
397   def invisible?(%User{invisible: true}), do: true
398   def invisible?(_), do: false
399
400   def avatar_url(user, options \\ []) do
401     case user.avatar do
402       %{"url" => [%{"href" => href} | _]} ->
403         href
404
405       _ ->
406         unless options[:no_default] do
407           Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
408         end
409     end
410   end
411
412   def banner_url(user, options \\ []) do
413     case user.banner do
414       %{"url" => [%{"href" => href} | _]} -> href
415       _ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
416     end
417   end
418
419   # Should probably be renamed or removed
420   @spec ap_id(User.t()) :: String.t()
421   def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
422
423   @spec ap_followers(User.t()) :: String.t()
424   def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
425   def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
426
427   @spec ap_following(User.t()) :: String.t()
428   def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
429   def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
430
431   @spec ap_featured_collection(User.t()) :: String.t()
432   def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa
433
434   def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured"
435
436   defp truncate_fields_param(params) do
437     if Map.has_key?(params, :fields) do
438       Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
439     else
440       params
441     end
442   end
443
444   defp truncate_if_exists(params, key, max_length) do
445     if Map.has_key?(params, key) and is_binary(params[key]) do
446       {value, _chopped} = String.split_at(params[key], max_length)
447       Map.put(params, key, value)
448     else
449       params
450     end
451   end
452
453   defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params
454
455   defp fix_follower_address(%{nickname: nickname} = params),
456     do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname}))
457
458   defp fix_follower_address(params), do: params
459
460   def remote_user_changeset(struct \\ %User{local: false}, params) do
461     bio_limit = Config.get([:instance, :user_bio_length], 5000)
462     name_limit = Config.get([:instance, :user_name_length], 100)
463
464     name =
465       case params[:name] do
466         name when is_binary(name) and byte_size(name) > 0 -> name
467         _ -> params[:nickname]
468       end
469
470     params =
471       params
472       |> Map.put(:name, name)
473       |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
474       |> truncate_if_exists(:name, name_limit)
475       |> truncate_if_exists(:bio, bio_limit)
476       |> truncate_fields_param()
477       |> fix_follower_address()
478
479     struct
480     |> cast(
481       params,
482       [
483         :bio,
484         :emoji,
485         :ap_id,
486         :inbox,
487         :shared_inbox,
488         :nickname,
489         :public_key,
490         :avatar,
491         :ap_enabled,
492         :banner,
493         :is_locked,
494         :last_refreshed_at,
495         :uri,
496         :follower_address,
497         :following_address,
498         :featured_address,
499         :hide_followers,
500         :hide_follows,
501         :hide_followers_count,
502         :hide_follows_count,
503         :follower_count,
504         :fields,
505         :following_count,
506         :is_discoverable,
507         :invisible,
508         :actor_type,
509         :also_known_as,
510         :accepts_chat_messages,
511         :pinned_objects,
512         :birthday,
513         :show_birthday
514       ]
515     )
516     |> cast(params, [:name], empty_values: [])
517     |> validate_required([:ap_id])
518     |> validate_required([:name], trim: false)
519     |> unique_constraint(:nickname)
520     |> validate_format(:nickname, @email_regex)
521     |> validate_length(:bio, max: bio_limit)
522     |> validate_length(:name, max: name_limit)
523     |> validate_fields(true)
524     |> validate_non_local()
525   end
526
527   defp validate_non_local(cng) do
528     local? = get_field(cng, :local)
529
530     if local? do
531       cng
532       |> add_error(:local, "User is local, can't update with this changeset.")
533     else
534       cng
535     end
536   end
537
538   def update_changeset(struct, params \\ %{}) do
539     bio_limit = Config.get([:instance, :user_bio_length], 5000)
540     name_limit = Config.get([:instance, :user_name_length], 100)
541
542     struct
543     |> cast(
544       params,
545       [
546         :bio,
547         :raw_bio,
548         :name,
549         :emoji,
550         :avatar,
551         :public_key,
552         :inbox,
553         :shared_inbox,
554         :is_locked,
555         :no_rich_text,
556         :default_scope,
557         :banner,
558         :hide_follows,
559         :hide_followers,
560         :hide_followers_count,
561         :hide_follows_count,
562         :hide_favorites,
563         :allow_following_move,
564         :also_known_as,
565         :background,
566         :show_role,
567         :skip_thread_containment,
568         :fields,
569         :raw_fields,
570         :pleroma_settings_store,
571         :is_discoverable,
572         :actor_type,
573         :accepts_chat_messages,
574         :disclose_client,
575         :birthday,
576         :show_birthday
577       ]
578     )
579     |> validate_min_age()
580     |> unique_constraint(:nickname)
581     |> validate_format(:nickname, local_nickname_regex())
582     |> validate_length(:bio, max: bio_limit)
583     |> validate_length(:name, min: 1, max: name_limit)
584     |> validate_inclusion(:actor_type, ["Person", "Service"])
585     |> put_fields()
586     |> put_emoji()
587     |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
588     |> put_change_if_present(:avatar, &put_upload(&1, :avatar))
589     |> put_change_if_present(:banner, &put_upload(&1, :banner))
590     |> put_change_if_present(:background, &put_upload(&1, :background))
591     |> put_change_if_present(
592       :pleroma_settings_store,
593       &{:ok, Map.merge(struct.pleroma_settings_store, &1)}
594     )
595     |> validate_fields(false)
596   end
597
598   defp put_fields(changeset) do
599     if raw_fields = get_change(changeset, :raw_fields) do
600       raw_fields =
601         raw_fields
602         |> Enum.filter(fn %{"name" => n} -> n != "" end)
603
604       fields =
605         raw_fields
606         |> Enum.map(fn f -> Map.update!(f, "value", &parse_fields(&1)) end)
607
608       changeset
609       |> put_change(:raw_fields, raw_fields)
610       |> put_change(:fields, fields)
611     else
612       changeset
613     end
614   end
615
616   defp parse_fields(value) do
617     value
618     |> Formatter.linkify(mentions_format: :full)
619     |> elem(0)
620   end
621
622   defp put_emoji(changeset) do
623     emojified_fields = [:bio, :name, :raw_fields]
624
625     if Enum.any?(changeset.changes, fn {k, _} -> k in emojified_fields end) do
626       bio = Emoji.Formatter.get_emoji_map(get_field(changeset, :bio))
627       name = Emoji.Formatter.get_emoji_map(get_field(changeset, :name))
628
629       emoji = Map.merge(bio, name)
630
631       emoji =
632         changeset
633         |> get_field(:raw_fields)
634         |> Enum.reduce(emoji, fn x, acc ->
635           Map.merge(acc, Emoji.Formatter.get_emoji_map(x["name"] <> x["value"]))
636         end)
637
638       put_change(changeset, :emoji, emoji)
639     else
640       changeset
641     end
642   end
643
644   defp put_change_if_present(changeset, map_field, value_function) do
645     with {:ok, value} <- fetch_change(changeset, map_field),
646          {:ok, new_value} <- value_function.(value) do
647       put_change(changeset, map_field, new_value)
648     else
649       {:error, :file_too_large} ->
650         Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value ->
651           [{map_field, "file is too large"}]
652         end)
653
654       _ ->
655         changeset
656     end
657   end
658
659   defp put_upload(value, type) do
660     with %Plug.Upload{} <- value,
661          {:ok, object} <- ActivityPub.upload(value, type: type) do
662       {:ok, object.data}
663     end
664   end
665
666   def update_as_admin_changeset(struct, params) do
667     struct
668     |> update_changeset(params)
669     |> cast(params, [:email])
670     |> delete_change(:also_known_as)
671     |> unique_constraint(:email)
672     |> validate_format(:email, @email_regex)
673     |> validate_inclusion(:actor_type, ["Person", "Service"])
674   end
675
676   @spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
677   def update_as_admin(user, params) do
678     params = Map.put(params, "password_confirmation", params["password"])
679     changeset = update_as_admin_changeset(user, params)
680
681     if params["password"] do
682       reset_password(user, changeset, params)
683     else
684       User.update_and_set_cache(changeset)
685     end
686   end
687
688   def password_update_changeset(struct, params) do
689     struct
690     |> cast(params, [:password, :password_confirmation])
691     |> validate_required([:password, :password_confirmation])
692     |> validate_confirmation(:password)
693     |> put_password_hash()
694     |> put_change(:password_reset_pending, false)
695   end
696
697   @spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
698   def reset_password(%User{} = user, params) do
699     reset_password(user, user, params)
700   end
701
702   def reset_password(%User{id: user_id} = user, struct, params) do
703     multi =
704       Multi.new()
705       |> Multi.update(:user, password_update_changeset(struct, params))
706       |> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
707       |> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
708
709     case Repo.transaction(multi) do
710       {:ok, %{user: user} = _} -> set_cache(user)
711       {:error, _, changeset, _} -> {:error, changeset}
712     end
713   end
714
715   def update_password_reset_pending(user, value) do
716     user
717     |> change()
718     |> put_change(:password_reset_pending, value)
719     |> update_and_set_cache()
720   end
721
722   def force_password_reset_async(user) do
723     BackgroundWorker.enqueue("force_password_reset", %{"user_id" => user.id})
724   end
725
726   @spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
727   def force_password_reset(user), do: update_password_reset_pending(user, true)
728
729   # Used to auto-register LDAP accounts which won't have a password hash stored locally
730   def register_changeset_ldap(struct, params = %{password: password})
731       when is_nil(password) do
732     params = Map.put_new(params, :accepts_chat_messages, true)
733
734     params =
735       if Map.has_key?(params, :email) do
736         Map.put_new(params, :email, params[:email])
737       else
738         params
739       end
740
741     struct
742     |> cast(params, [
743       :name,
744       :nickname,
745       :email,
746       :accepts_chat_messages
747     ])
748     |> validate_required([:name, :nickname])
749     |> unique_constraint(:nickname)
750     |> validate_not_restricted_nickname(:nickname)
751     |> validate_format(:nickname, local_nickname_regex())
752     |> put_ap_id()
753     |> unique_constraint(:ap_id)
754     |> put_following_and_follower_and_featured_address()
755     |> put_private_key()
756   end
757
758   def register_changeset(struct, params \\ %{}, opts \\ []) do
759     bio_limit = Config.get([:instance, :user_bio_length], 5000)
760     name_limit = Config.get([:instance, :user_name_length], 100)
761     reason_limit = Config.get([:instance, :registration_reason_length], 500)
762     params = Map.put_new(params, :accepts_chat_messages, true)
763
764     confirmed? =
765       if is_nil(opts[:confirmed]) do
766         !Config.get([:instance, :account_activation_required])
767       else
768         opts[:confirmed]
769       end
770
771     approved? =
772       if is_nil(opts[:approved]) do
773         !Config.get([:instance, :account_approval_required])
774       else
775         opts[:approved]
776       end
777
778     struct
779     |> confirmation_changeset(set_confirmation: confirmed?)
780     |> approval_changeset(set_approval: approved?)
781     |> cast(params, [
782       :bio,
783       :raw_bio,
784       :email,
785       :name,
786       :nickname,
787       :password,
788       :password_confirmation,
789       :emoji,
790       :accepts_chat_messages,
791       :registration_reason,
792       :birthday,
793       :language
794     ])
795     |> validate_required([:name, :nickname, :password, :password_confirmation])
796     |> validate_confirmation(:password)
797     |> unique_constraint(:email)
798     |> validate_format(:email, @email_regex)
799     |> validate_email_not_in_blacklisted_domain(:email)
800     |> unique_constraint(:nickname)
801     |> validate_not_restricted_nickname(:nickname)
802     |> validate_format(:nickname, local_nickname_regex())
803     |> validate_length(:bio, max: bio_limit)
804     |> validate_length(:name, min: 1, max: name_limit)
805     |> validate_length(:registration_reason, max: reason_limit)
806     |> maybe_validate_required_email(opts[:external])
807     |> maybe_validate_required_birthday
808     |> validate_min_age()
809     |> put_password_hash
810     |> put_ap_id()
811     |> unique_constraint(:ap_id)
812     |> put_following_and_follower_and_featured_address()
813     |> put_private_key()
814   end
815
816   def validate_not_restricted_nickname(changeset, field) do
817     validate_change(changeset, field, fn _, value ->
818       valid? =
819         Config.get([User, :restricted_nicknames])
820         |> Enum.all?(fn restricted_nickname ->
821           String.downcase(value) != String.downcase(restricted_nickname)
822         end)
823
824       if valid?, do: [], else: [nickname: "Invalid nickname"]
825     end)
826   end
827
828   def validate_email_not_in_blacklisted_domain(changeset, field) do
829     validate_change(changeset, field, fn _, value ->
830       valid? =
831         Config.get([User, :email_blacklist])
832         |> Enum.all?(fn blacklisted_domain ->
833           blacklisted_domain_downcase = String.downcase(blacklisted_domain)
834
835           !String.ends_with?(String.downcase(value), [
836             "@" <> blacklisted_domain_downcase,
837             "." <> blacklisted_domain_downcase
838           ])
839         end)
840
841       if valid?, do: [], else: [email: "Invalid email"]
842     end)
843   end
844
845   def maybe_validate_required_email(changeset, true), do: changeset
846
847   def maybe_validate_required_email(changeset, _) do
848     if Config.get([:instance, :account_activation_required]) do
849       validate_required(changeset, [:email])
850     else
851       changeset
852     end
853   end
854
855   defp maybe_validate_required_birthday(changeset) do
856     if Config.get([:instance, :birthday_required]) do
857       validate_required(changeset, [:birthday])
858     else
859       changeset
860     end
861   end
862
863   defp validate_min_age(changeset) do
864     changeset
865     |> validate_change(:birthday, fn :birthday, birthday ->
866       valid? =
867         Date.utc_today()
868         |> Date.diff(birthday) >=
869           Config.get([:instance, :birthday_min_age])
870
871       if valid?, do: [], else: [birthday: "Invalid age"]
872     end)
873   end
874
875   defp put_ap_id(changeset) do
876     ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
877     put_change(changeset, :ap_id, ap_id)
878   end
879
880   defp put_following_and_follower_and_featured_address(changeset) do
881     user = %User{nickname: get_field(changeset, :nickname)}
882     followers = ap_followers(user)
883     following = ap_following(user)
884     featured = ap_featured_collection(user)
885
886     changeset
887     |> put_change(:follower_address, followers)
888     |> put_change(:following_address, following)
889     |> put_change(:featured_address, featured)
890   end
891
892   defp put_private_key(changeset) do
893     {:ok, pem} = Keys.generate_rsa_pem()
894     put_change(changeset, :keys, pem)
895   end
896
897   defp autofollow_users(user) do
898     candidates = Config.get([:instance, :autofollowed_nicknames])
899
900     autofollowed_users =
901       User.Query.build(%{nickname: candidates, local: true, is_active: true})
902       |> Repo.all()
903
904     follow_all(user, autofollowed_users)
905   end
906
907   defp autofollowing_users(user) do
908     candidates = Config.get([:instance, :autofollowing_nicknames])
909
910     User.Query.build(%{nickname: candidates, local: true, deactivated: false})
911     |> Repo.all()
912     |> Enum.each(&follow(&1, user, :follow_accept))
913
914     {:ok, :success}
915   end
916
917   @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
918   def register(%Ecto.Changeset{} = changeset) do
919     with {:ok, user} <- Repo.insert(changeset) do
920       post_register_action(user)
921     end
922   end
923
924   def post_register_action(%User{is_confirmed: false} = user) do
925     with {:ok, _} <- maybe_send_confirmation_email(user) do
926       {:ok, user}
927     end
928   end
929
930   def post_register_action(%User{is_approved: false} = user) do
931     with {:ok, _} <- send_user_approval_email(user),
932          {:ok, _} <- send_admin_approval_emails(user) do
933       {:ok, user}
934     end
935   end
936
937   def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do
938     with {:ok, user} <- autofollow_users(user),
939          {:ok, _} <- autofollowing_users(user),
940          {:ok, user} <- set_cache(user),
941          {:ok, _} <- maybe_send_registration_email(user),
942          {:ok, _} <- maybe_send_welcome_email(user),
943          {:ok, _} <- maybe_send_welcome_message(user),
944          {:ok, _} <- maybe_send_welcome_chat_message(user) do
945       {:ok, user}
946     end
947   end
948
949   defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do
950     user
951     |> Pleroma.Emails.UserEmail.approval_pending_email()
952     |> Pleroma.Emails.Mailer.deliver_async()
953
954     {:ok, :enqueued}
955   end
956
957   defp send_user_approval_email(_user) do
958     {:ok, :skipped}
959   end
960
961   defp send_admin_approval_emails(user) do
962     all_superusers()
963     |> Enum.filter(fn user -> not is_nil(user.email) end)
964     |> Enum.each(fn superuser ->
965       superuser
966       |> Pleroma.Emails.AdminEmail.new_unapproved_registration(user)
967       |> Pleroma.Emails.Mailer.deliver_async()
968     end)
969
970     {:ok, :enqueued}
971   end
972
973   defp maybe_send_welcome_message(user) do
974     if User.WelcomeMessage.enabled?() do
975       User.WelcomeMessage.post_message(user)
976       {:ok, :enqueued}
977     else
978       {:ok, :noop}
979     end
980   end
981
982   defp maybe_send_welcome_chat_message(user) do
983     if User.WelcomeChatMessage.enabled?() do
984       User.WelcomeChatMessage.post_message(user)
985       {:ok, :enqueued}
986     else
987       {:ok, :noop}
988     end
989   end
990
991   defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do
992     if User.WelcomeEmail.enabled?() do
993       User.WelcomeEmail.send_email(user)
994       {:ok, :enqueued}
995     else
996       {:ok, :noop}
997     end
998   end
999
1000   defp maybe_send_welcome_email(_), do: {:ok, :noop}
1001
1002   @spec maybe_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop}
1003   def maybe_send_confirmation_email(%User{is_confirmed: false, email: email} = user)
1004       when is_binary(email) do
1005     if Config.get([:instance, :account_activation_required]) do
1006       send_confirmation_email(user)
1007       {:ok, :enqueued}
1008     else
1009       {:ok, :noop}
1010     end
1011   end
1012
1013   def maybe_send_confirmation_email(_), do: {:ok, :noop}
1014
1015   @spec send_confirmation_email(Uset.t()) :: User.t()
1016   def send_confirmation_email(%User{} = user) do
1017     user
1018     |> Pleroma.Emails.UserEmail.account_confirmation_email()
1019     |> Pleroma.Emails.Mailer.deliver_async()
1020
1021     user
1022   end
1023
1024   @spec maybe_send_registration_email(User.t()) :: {:ok, :enqueued | :noop}
1025   defp maybe_send_registration_email(%User{email: email} = user) when is_binary(email) do
1026     with false <- User.WelcomeEmail.enabled?(),
1027          false <- Config.get([:instance, :account_activation_required], false),
1028          false <- Config.get([:instance, :account_approval_required], false) do
1029       user
1030       |> Pleroma.Emails.UserEmail.successful_registration_email()
1031       |> Pleroma.Emails.Mailer.deliver_async()
1032
1033       {:ok, :enqueued}
1034     else
1035       _ ->
1036         {:ok, :noop}
1037     end
1038   end
1039
1040   defp maybe_send_registration_email(_), do: {:ok, :noop}
1041
1042   def needs_update?(%User{local: true}), do: false
1043
1044   def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
1045
1046   def needs_update?(%User{local: false} = user) do
1047     NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86_400
1048   end
1049
1050   def needs_update?(_), do: true
1051
1052   @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
1053
1054   # "Locked" (self-locked) users demand explicit authorization of follow requests
1055   def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
1056     follow(follower, followed, :follow_pending)
1057   end
1058
1059   def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
1060     follow(follower, followed)
1061   end
1062
1063   def maybe_direct_follow(%User{} = follower, %User{} = followed) do
1064     if not ap_enabled?(followed) do
1065       follow(follower, followed)
1066     else
1067       {:ok, follower, followed}
1068     end
1069   end
1070
1071   @doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
1072   @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
1073   def follow_all(follower, followeds) do
1074     followeds
1075     |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
1076     |> Enum.each(&follow(follower, &1, :follow_accept))
1077
1078     set_cache(follower)
1079   end
1080
1081   def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
1082     deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
1083
1084     cond do
1085       not followed.is_active ->
1086         {:error, "Could not follow user: #{followed.nickname} is deactivated."}
1087
1088       deny_follow_blocked and blocks?(followed, follower) ->
1089         {:error, "Could not follow user: #{followed.nickname} blocked you."}
1090
1091       true ->
1092         FollowingRelationship.follow(follower, followed, state)
1093     end
1094   end
1095
1096   def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
1097     {:error, "Not subscribed!"}
1098   end
1099
1100   @spec unfollow(User.t(), User.t()) :: {:ok, User.t(), Activity.t()} | {:error, String.t()}
1101   def unfollow(%User{} = follower, %User{} = followed) do
1102     case do_unfollow(follower, followed) do
1103       {:ok, follower, followed} ->
1104         {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
1105
1106       error ->
1107         error
1108     end
1109   end
1110
1111   @spec do_unfollow(User.t(), User.t()) :: {:ok, User.t(), User.t()} | {:error, String.t()}
1112   defp do_unfollow(%User{} = follower, %User{} = followed) do
1113     case get_follow_state(follower, followed) do
1114       state when state in [:follow_pending, :follow_accept] ->
1115         FollowingRelationship.unfollow(follower, followed)
1116
1117       nil ->
1118         {:error, "Not subscribed!"}
1119     end
1120   end
1121
1122   @doc "Returns follow state as Pleroma.FollowingRelationship.State value"
1123   def get_follow_state(%User{} = follower, %User{} = following) do
1124     following_relationship = FollowingRelationship.get(follower, following)
1125     get_follow_state(follower, following, following_relationship)
1126   end
1127
1128   def get_follow_state(
1129         %User{} = follower,
1130         %User{} = following,
1131         following_relationship
1132       ) do
1133     case {following_relationship, following.local} do
1134       {nil, false} ->
1135         case Utils.fetch_latest_follow(follower, following) do
1136           %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
1137             FollowingRelationship.state_to_enum(state)
1138
1139           _ ->
1140             nil
1141         end
1142
1143       {%{state: state}, _} ->
1144         state
1145
1146       {nil, _} ->
1147         nil
1148     end
1149   end
1150
1151   def locked?(%User{} = user) do
1152     user.is_locked || false
1153   end
1154
1155   def get_by_id(id) do
1156     Repo.get_by(User, id: id)
1157   end
1158
1159   def get_by_ap_id(ap_id) do
1160     Repo.get_by(User, ap_id: ap_id)
1161   end
1162
1163   def get_by_uri(uri) do
1164     Repo.get_by(User, uri: uri)
1165   end
1166
1167   def get_all_by_ap_id(ap_ids) do
1168     from(u in __MODULE__,
1169       where: u.ap_id in ^ap_ids
1170     )
1171     |> Repo.all()
1172   end
1173
1174   def get_all_by_ids(ids) do
1175     from(u in __MODULE__, where: u.id in ^ids)
1176     |> Repo.all()
1177   end
1178
1179   # This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
1180   # of the ap_id and the domain and tries to get that user
1181   def get_by_guessed_nickname(ap_id) do
1182     domain = URI.parse(ap_id).host
1183     name = List.last(String.split(ap_id, "/"))
1184     nickname = "#{name}@#{domain}"
1185
1186     get_cached_by_nickname(nickname)
1187   end
1188
1189   def set_cache({:ok, user}), do: set_cache(user)
1190   def set_cache({:error, err}), do: {:error, err}
1191
1192   def set_cache(%User{} = user) do
1193     @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
1194     @cachex.put(:user_cache, "nickname:#{user.nickname}", user)
1195     @cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
1196     {:ok, user}
1197   end
1198
1199   def update_and_set_cache(struct, params) do
1200     struct
1201     |> update_changeset(params)
1202     |> update_and_set_cache()
1203   end
1204
1205   def update_and_set_cache(changeset) do
1206     with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
1207       set_cache(user)
1208     end
1209   end
1210
1211   def get_user_friends_ap_ids(user) do
1212     from(u in User.get_friends_query(user), select: u.ap_id)
1213     |> Repo.all()
1214   end
1215
1216   @spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()]
1217   def get_cached_user_friends_ap_ids(user) do
1218     @cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
1219       get_user_friends_ap_ids(user)
1220     end)
1221   end
1222
1223   def invalidate_cache(user) do
1224     @cachex.del(:user_cache, "ap_id:#{user.ap_id}")
1225     @cachex.del(:user_cache, "nickname:#{user.nickname}")
1226     @cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
1227     @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
1228     @cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}")
1229   end
1230
1231   @spec get_cached_by_ap_id(String.t()) :: User.t() | nil
1232   def get_cached_by_ap_id(ap_id) do
1233     key = "ap_id:#{ap_id}"
1234
1235     with {:ok, nil} <- @cachex.get(:user_cache, key),
1236          user when not is_nil(user) <- get_by_ap_id(ap_id),
1237          {:ok, true} <- @cachex.put(:user_cache, key, user) do
1238       user
1239     else
1240       {:ok, user} -> user
1241       nil -> nil
1242     end
1243   end
1244
1245   def get_cached_by_id(id) do
1246     key = "id:#{id}"
1247
1248     ap_id =
1249       @cachex.fetch!(:user_cache, key, fn _ ->
1250         user = get_by_id(id)
1251
1252         if user do
1253           @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
1254           {:commit, user.ap_id}
1255         else
1256           {:ignore, ""}
1257         end
1258       end)
1259
1260     get_cached_by_ap_id(ap_id)
1261   end
1262
1263   def get_cached_by_nickname(nickname) do
1264     key = "nickname:#{nickname}"
1265
1266     @cachex.fetch!(:user_cache, key, fn _ ->
1267       case get_or_fetch_by_nickname(nickname) do
1268         {:ok, user} -> {:commit, user}
1269         {:error, _error} -> {:ignore, nil}
1270       end
1271     end)
1272   end
1273
1274   def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do
1275     restrict_to_local = Config.get([:instance, :limit_to_local_content])
1276
1277     cond do
1278       is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
1279         get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id)
1280
1281       restrict_to_local == false or not String.contains?(nickname_or_id, "@") ->
1282         get_cached_by_nickname(nickname_or_id)
1283
1284       restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) ->
1285         get_cached_by_nickname(nickname_or_id)
1286
1287       true ->
1288         nil
1289     end
1290   end
1291
1292   @spec get_by_nickname(String.t()) :: User.t() | nil
1293   def get_by_nickname(nickname) do
1294     Repo.get_by(User, nickname: nickname) ||
1295       if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
1296         Repo.get_by(User, nickname: local_nickname(nickname))
1297       end
1298   end
1299
1300   def get_by_email(email), do: Repo.get_by(User, email: email)
1301
1302   def get_by_nickname_or_email(nickname_or_email) do
1303     get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
1304   end
1305
1306   def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
1307
1308   def get_or_fetch_by_nickname(nickname) do
1309     with %User{} = user <- get_by_nickname(nickname) do
1310       {:ok, user}
1311     else
1312       _e ->
1313         with [_nick, _domain] <- String.split(nickname, "@"),
1314              {:ok, user} <- fetch_by_nickname(nickname) do
1315           {:ok, user}
1316         else
1317           _e -> {:error, "not found " <> nickname}
1318         end
1319     end
1320   end
1321
1322   @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
1323   def get_followers_query(%User{} = user, nil) do
1324     User.Query.build(%{followers: user, is_active: true})
1325   end
1326
1327   def get_followers_query(%User{} = user, page) do
1328     user
1329     |> get_followers_query(nil)
1330     |> User.Query.paginate(page, 20)
1331   end
1332
1333   @spec get_followers_query(User.t()) :: Ecto.Query.t()
1334   def get_followers_query(%User{} = user), do: get_followers_query(user, nil)
1335
1336   @spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
1337   def get_followers(%User{} = user, page \\ nil) do
1338     user
1339     |> get_followers_query(page)
1340     |> Repo.all()
1341   end
1342
1343   @spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
1344   def get_external_followers(%User{} = user, page \\ nil) do
1345     user
1346     |> get_followers_query(page)
1347     |> User.Query.build(%{external: true})
1348     |> Repo.all()
1349   end
1350
1351   def get_followers_ids(%User{} = user, page \\ nil) do
1352     user
1353     |> get_followers_query(page)
1354     |> select([u], u.id)
1355     |> Repo.all()
1356   end
1357
1358   @spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
1359   def get_friends_query(%User{} = user, nil) do
1360     User.Query.build(%{friends: user, deactivated: false})
1361   end
1362
1363   def get_friends_query(%User{} = user, page) do
1364     user
1365     |> get_friends_query(nil)
1366     |> User.Query.paginate(page, 20)
1367   end
1368
1369   @spec get_friends_query(User.t()) :: Ecto.Query.t()
1370   def get_friends_query(%User{} = user), do: get_friends_query(user, nil)
1371
1372   def get_friends(%User{} = user, page \\ nil) do
1373     user
1374     |> get_friends_query(page)
1375     |> Repo.all()
1376   end
1377
1378   def get_friends_ap_ids(%User{} = user) do
1379     user
1380     |> get_friends_query(nil)
1381     |> select([u], u.ap_id)
1382     |> Repo.all()
1383   end
1384
1385   def get_friends_ids(%User{} = user, page \\ nil) do
1386     user
1387     |> get_friends_query(page)
1388     |> select([u], u.id)
1389     |> Repo.all()
1390   end
1391
1392   def increase_note_count(%User{} = user) do
1393     User
1394     |> where(id: ^user.id)
1395     |> update([u], inc: [note_count: 1])
1396     |> select([u], u)
1397     |> Repo.update_all([])
1398     |> case do
1399       {1, [user]} -> set_cache(user)
1400       _ -> {:error, user}
1401     end
1402   end
1403
1404   def decrease_note_count(%User{} = user) do
1405     User
1406     |> where(id: ^user.id)
1407     |> update([u],
1408       set: [
1409         note_count: fragment("greatest(0, note_count - 1)")
1410       ]
1411     )
1412     |> select([u], u)
1413     |> Repo.update_all([])
1414     |> case do
1415       {1, [user]} -> set_cache(user)
1416       _ -> {:error, user}
1417     end
1418   end
1419
1420   def update_note_count(%User{} = user, note_count \\ nil) do
1421     note_count =
1422       note_count ||
1423         from(
1424           a in Object,
1425           where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
1426           select: count(a.id)
1427         )
1428         |> Repo.one()
1429
1430     user
1431     |> cast(%{note_count: note_count}, [:note_count])
1432     |> update_and_set_cache()
1433   end
1434
1435   @spec maybe_fetch_follow_information(User.t()) :: User.t()
1436   def maybe_fetch_follow_information(user) do
1437     with {:ok, user} <- fetch_follow_information(user) do
1438       user
1439     else
1440       e ->
1441         Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}")
1442
1443         user
1444     end
1445   end
1446
1447   def fetch_follow_information(user) do
1448     with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
1449       user
1450       |> follow_information_changeset(info)
1451       |> update_and_set_cache()
1452     end
1453   end
1454
1455   defp follow_information_changeset(user, params) do
1456     user
1457     |> cast(params, [
1458       :hide_followers,
1459       :hide_follows,
1460       :follower_count,
1461       :following_count,
1462       :hide_followers_count,
1463       :hide_follows_count
1464     ])
1465   end
1466
1467   @spec update_follower_count(User.t()) :: {:ok, User.t()}
1468   def update_follower_count(%User{} = user) do
1469     if user.local or !Config.get([:instance, :external_user_synchronization]) do
1470       follower_count = FollowingRelationship.follower_count(user)
1471
1472       user
1473       |> follow_information_changeset(%{follower_count: follower_count})
1474       |> update_and_set_cache
1475     else
1476       {:ok, maybe_fetch_follow_information(user)}
1477     end
1478   end
1479
1480   @spec update_following_count(User.t()) :: {:ok, User.t()}
1481   def update_following_count(%User{local: false} = user) do
1482     if Config.get([:instance, :external_user_synchronization]) do
1483       {:ok, maybe_fetch_follow_information(user)}
1484     else
1485       {:ok, user}
1486     end
1487   end
1488
1489   def update_following_count(%User{local: true} = user) do
1490     following_count = FollowingRelationship.following_count(user)
1491
1492     user
1493     |> follow_information_changeset(%{following_count: following_count})
1494     |> update_and_set_cache()
1495   end
1496
1497   @spec get_users_from_set([String.t()], keyword()) :: [User.t()]
1498   def get_users_from_set(ap_ids, opts \\ []) do
1499     local_only = Keyword.get(opts, :local_only, true)
1500     criteria = %{ap_id: ap_ids, is_active: true}
1501     criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
1502
1503     User.Query.build(criteria)
1504     |> Repo.all()
1505   end
1506
1507   @spec get_recipients_from_activity(Activity.t()) :: [User.t()]
1508   def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
1509     to = [actor | to]
1510
1511     query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true})
1512
1513     query
1514     |> Repo.all()
1515   end
1516
1517   @spec mute(User.t(), User.t(), map()) ::
1518           {:ok, list(UserRelationship.t())} | {:error, String.t()}
1519   def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
1520     notifications? = Map.get(params, :notifications, true)
1521     duration = Map.get(params, :duration, 0)
1522
1523     expires_at =
1524       if duration > 0 do
1525         DateTime.utc_now()
1526         |> DateTime.add(duration)
1527       else
1528         nil
1529       end
1530
1531     with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at),
1532          {:ok, user_notification_mute} <-
1533            (notifications? &&
1534               UserRelationship.create_notification_mute(
1535                 muter,
1536                 mutee,
1537                 expires_at
1538               )) ||
1539              {:ok, nil} do
1540       if duration > 0 do
1541         Pleroma.Workers.MuteExpireWorker.enqueue(
1542           "unmute_user",
1543           %{"muter_id" => muter.id, "mutee_id" => mutee.id},
1544           scheduled_at: expires_at
1545         )
1546       end
1547
1548       @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
1549
1550       {:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
1551     end
1552   end
1553
1554   def unmute(%User{} = muter, %User{} = mutee) do
1555     with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
1556          {:ok, user_notification_mute} <-
1557            UserRelationship.delete_notification_mute(muter, mutee) do
1558       @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
1559       {:ok, [user_mute, user_notification_mute]}
1560     end
1561   end
1562
1563   def unmute(muter_id, mutee_id) do
1564     with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)},
1565          {:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do
1566       unmute(muter, mutee)
1567     else
1568       {who, result} = error ->
1569         Logger.warn(
1570           "User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
1571         )
1572
1573         {:error, error}
1574     end
1575   end
1576
1577   def subscribe(%User{} = subscriber, %User{} = target) do
1578     deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
1579
1580     if blocks?(target, subscriber) and deny_follow_blocked do
1581       {:error, "Could not subscribe: #{target.nickname} is blocking you"}
1582     else
1583       # Note: the relationship is inverse: subscriber acts as relationship target
1584       UserRelationship.create_inverse_subscription(target, subscriber)
1585     end
1586   end
1587
1588   def subscribe(%User{} = subscriber, %{ap_id: ap_id}) do
1589     with %User{} = subscribee <- get_cached_by_ap_id(ap_id) do
1590       subscribe(subscriber, subscribee)
1591     end
1592   end
1593
1594   def unsubscribe(%User{} = unsubscriber, %User{} = target) do
1595     # Note: the relationship is inverse: subscriber acts as relationship target
1596     UserRelationship.delete_inverse_subscription(target, unsubscriber)
1597   end
1598
1599   def unsubscribe(%User{} = unsubscriber, %{ap_id: ap_id}) do
1600     with %User{} = user <- get_cached_by_ap_id(ap_id) do
1601       unsubscribe(unsubscriber, user)
1602     end
1603   end
1604
1605   def block(%User{} = blocker, %User{} = blocked) do
1606     # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
1607     blocker =
1608       if following?(blocker, blocked) do
1609         {:ok, blocker, _} = unfollow(blocker, blocked)
1610         blocker
1611       else
1612         blocker
1613       end
1614
1615     # clear any requested follows from both sides as well
1616     blocked =
1617       case CommonAPI.reject_follow_request(blocked, blocker) do
1618         {:ok, %User{} = updated_blocked} -> updated_blocked
1619         nil -> blocked
1620       end
1621
1622     blocker =
1623       case CommonAPI.reject_follow_request(blocker, blocked) do
1624         {:ok, %User{} = updated_blocker} -> updated_blocker
1625         nil -> blocker
1626       end
1627
1628     unsubscribe(blocked, blocker)
1629
1630     unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
1631     if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker)
1632
1633     {:ok, blocker} = update_follower_count(blocker)
1634     {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
1635     add_to_block(blocker, blocked)
1636   end
1637
1638   # helper to handle the block given only an actor's AP id
1639   def block(%User{} = blocker, %{ap_id: ap_id}) do
1640     block(blocker, get_cached_by_ap_id(ap_id))
1641   end
1642
1643   def unblock(%User{} = blocker, %User{} = blocked) do
1644     remove_from_block(blocker, blocked)
1645   end
1646
1647   # helper to handle the block given only an actor's AP id
1648   def unblock(%User{} = blocker, %{ap_id: ap_id}) do
1649     unblock(blocker, get_cached_by_ap_id(ap_id))
1650   end
1651
1652   def endorse(%User{} = endorser, %User{} = target) do
1653     with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
1654          endorsed_users <-
1655            User.endorsed_users_relation(endorser)
1656            |> Repo.aggregate(:count, :id) do
1657       cond do
1658         endorsed_users >= max_endorsed_users ->
1659           {:error, "You have already pinned the maximum number of users"}
1660
1661         not following?(endorser, target) ->
1662           {:error, "Could not endorse: You are not following #{target.nickname}"}
1663
1664         true ->
1665           UserRelationship.create_endorsement(endorser, target)
1666       end
1667     end
1668   end
1669
1670   def endorse(%User{} = endorser, %{ap_id: ap_id}) do
1671     with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
1672       endorse(endorser, endorsed)
1673     end
1674   end
1675
1676   def unendorse(%User{} = unendorser, %User{} = target) do
1677     UserRelationship.delete_endorsement(unendorser, target)
1678   end
1679
1680   def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
1681     with %User{} = user <- get_cached_by_ap_id(ap_id) do
1682       unendorse(unendorser, user)
1683     end
1684   end
1685
1686   def mutes?(nil, _), do: false
1687   def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
1688
1689   def mutes_user?(%User{} = user, %User{} = target) do
1690     UserRelationship.mute_exists?(user, target)
1691   end
1692
1693   @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
1694   def muted_notifications?(nil, _), do: false
1695
1696   def muted_notifications?(%User{} = user, %User{} = target),
1697     do: UserRelationship.notification_mute_exists?(user, target)
1698
1699   def blocks?(nil, _), do: false
1700
1701   def blocks?(%User{} = user, %User{} = target) do
1702     blocks_user?(user, target) ||
1703       (blocks_domain?(user, target) and not User.following?(user, target))
1704   end
1705
1706   def blocks_user?(%User{} = user, %User{} = target) do
1707     UserRelationship.block_exists?(user, target)
1708   end
1709
1710   def blocks_user?(_, _), do: false
1711
1712   def blocks_domain?(%User{} = user, %User{} = target) do
1713     domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
1714     %{host: host} = URI.parse(target.ap_id)
1715     Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
1716   end
1717
1718   def blocks_domain?(_, _), do: false
1719
1720   def subscribed_to?(%User{} = user, %User{} = target) do
1721     # Note: the relationship is inverse: subscriber acts as relationship target
1722     UserRelationship.inverse_subscription_exists?(target, user)
1723   end
1724
1725   def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
1726     with %User{} = target <- get_cached_by_ap_id(ap_id) do
1727       subscribed_to?(user, target)
1728     end
1729   end
1730
1731   def endorses?(%User{} = user, %User{} = target) do
1732     UserRelationship.endorsement_exists?(user, target)
1733   end
1734
1735   @doc """
1736   Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
1737   E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
1738   """
1739   @spec outgoing_relationships_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
1740   def outgoing_relationships_ap_ids(_user, []), do: %{}
1741
1742   def outgoing_relationships_ap_ids(nil, _relationship_types), do: %{}
1743
1744   def outgoing_relationships_ap_ids(%User{} = user, relationship_types)
1745       when is_list(relationship_types) do
1746     db_result =
1747       user
1748       |> assoc(:outgoing_relationships)
1749       |> join(:inner, [user_rel], u in assoc(user_rel, :target))
1750       |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
1751       |> select([user_rel, u], [user_rel.relationship_type, fragment("array_agg(?)", u.ap_id)])
1752       |> group_by([user_rel, u], user_rel.relationship_type)
1753       |> Repo.all()
1754       |> Enum.into(%{}, fn [k, v] -> {k, v} end)
1755
1756     Enum.into(
1757       relationship_types,
1758       %{},
1759       fn rel_type -> {rel_type, db_result[rel_type] || []} end
1760     )
1761   end
1762
1763   def incoming_relationships_ungrouped_ap_ids(user, relationship_types, ap_ids \\ nil)
1764
1765   def incoming_relationships_ungrouped_ap_ids(_user, [], _ap_ids), do: []
1766
1767   def incoming_relationships_ungrouped_ap_ids(nil, _relationship_types, _ap_ids), do: []
1768
1769   def incoming_relationships_ungrouped_ap_ids(%User{} = user, relationship_types, ap_ids)
1770       when is_list(relationship_types) do
1771     user
1772     |> assoc(:incoming_relationships)
1773     |> join(:inner, [user_rel], u in assoc(user_rel, :source))
1774     |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
1775     |> maybe_filter_on_ap_id(ap_ids)
1776     |> select([user_rel, u], u.ap_id)
1777     |> distinct(true)
1778     |> Repo.all()
1779   end
1780
1781   defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do
1782     where(query, [user_rel, u], u.ap_id in ^ap_ids)
1783   end
1784
1785   defp maybe_filter_on_ap_id(query, _ap_ids), do: query
1786
1787   def set_activation_async(user, status \\ true) do
1788     BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status})
1789   end
1790
1791   @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
1792   def set_activation(users, status) when is_list(users) do
1793     Repo.transaction(fn ->
1794       for user <- users, do: set_activation(user, status)
1795     end)
1796   end
1797
1798   @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
1799   def set_activation(%User{} = user, status) do
1800     with {:ok, user} <- set_activation_status(user, status) do
1801       user
1802       |> get_followers()
1803       |> Enum.filter(& &1.local)
1804       |> Enum.each(&set_cache(update_following_count(&1)))
1805
1806       # Only update local user counts, remote will be update during the next pull.
1807       user
1808       |> get_friends()
1809       |> Enum.filter(& &1.local)
1810       |> Enum.each(&do_unfollow(user, &1))
1811
1812       {:ok, user}
1813     end
1814   end
1815
1816   def approve(users) when is_list(users) do
1817     Repo.transaction(fn ->
1818       Enum.map(users, fn user ->
1819         with {:ok, user} <- approve(user), do: user
1820       end)
1821     end)
1822   end
1823
1824   def approve(%User{is_approved: false} = user) do
1825     with chg <- change(user, is_approved: true),
1826          {:ok, user} <- update_and_set_cache(chg) do
1827       post_register_action(user)
1828       {:ok, user}
1829     end
1830   end
1831
1832   def approve(%User{} = user), do: {:ok, user}
1833
1834   def confirm(users) when is_list(users) do
1835     Repo.transaction(fn ->
1836       Enum.map(users, fn user ->
1837         with {:ok, user} <- confirm(user), do: user
1838       end)
1839     end)
1840   end
1841
1842   def confirm(%User{is_confirmed: false} = user) do
1843     with chg <- confirmation_changeset(user, set_confirmation: true),
1844          {:ok, user} <- update_and_set_cache(chg) do
1845       post_register_action(user)
1846       {:ok, user}
1847     end
1848   end
1849
1850   def confirm(%User{} = user), do: {:ok, user}
1851
1852   def set_suggestion(users, is_suggested) when is_list(users) do
1853     Repo.transaction(fn ->
1854       Enum.map(users, fn user ->
1855         with {:ok, user} <- set_suggestion(user, is_suggested), do: user
1856       end)
1857     end)
1858   end
1859
1860   def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
1861
1862   def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
1863     user
1864     |> change(is_suggested: is_suggested)
1865     |> update_and_set_cache()
1866   end
1867
1868   def update_notification_settings(%User{} = user, settings) do
1869     user
1870     |> cast(%{notification_settings: settings}, [])
1871     |> cast_embed(:notification_settings)
1872     |> validate_required([:notification_settings])
1873     |> update_and_set_cache()
1874   end
1875
1876   @spec purge_user_changeset(User.t()) :: Changeset.t()
1877   def purge_user_changeset(user) do
1878     # "Right to be forgotten"
1879     # https://gdpr.eu/right-to-be-forgotten/
1880     change(user, %{
1881       bio: "",
1882       raw_bio: nil,
1883       email: nil,
1884       name: nil,
1885       password_hash: nil,
1886       avatar: %{},
1887       tags: [],
1888       last_refreshed_at: nil,
1889       last_digest_emailed_at: nil,
1890       banner: %{},
1891       background: %{},
1892       note_count: 0,
1893       follower_count: 0,
1894       following_count: 0,
1895       is_locked: false,
1896       password_reset_pending: false,
1897       registration_reason: nil,
1898       confirmation_token: nil,
1899       domain_blocks: [],
1900       is_active: false,
1901       ap_enabled: false,
1902       is_moderator: false,
1903       is_admin: false,
1904       mascot: nil,
1905       emoji: %{},
1906       pleroma_settings_store: %{},
1907       fields: [],
1908       raw_fields: [],
1909       is_discoverable: false,
1910       also_known_as: []
1911       # id: preserved
1912       # ap_id: preserved
1913       # nickname: preserved
1914     })
1915   end
1916
1917   # Purge doesn't delete the user from the database.
1918   # It just nulls all its fields and deactivates it.
1919   # See `User.purge_user_changeset/1` above.
1920   defp purge(%User{} = user) do
1921     user
1922     |> purge_user_changeset()
1923     |> update_and_set_cache()
1924   end
1925
1926   def delete(users) when is_list(users) do
1927     for user <- users, do: delete(user)
1928   end
1929
1930   def delete(%User{} = user) do
1931     # Purge the user immediately
1932     purge(user)
1933     BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
1934   end
1935
1936   # *Actually* delete the user from the DB
1937   defp delete_from_db(%User{} = user) do
1938     invalidate_cache(user)
1939     Repo.delete(user)
1940   end
1941
1942   # If the user never finalized their account, it's safe to delete them.
1943   defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
1944     do: delete_from_db(user)
1945
1946   defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
1947     do: delete_from_db(user)
1948
1949   defp maybe_delete_from_db(user), do: {:ok, user}
1950
1951   def perform(:force_password_reset, user), do: force_password_reset(user)
1952
1953   @spec perform(atom(), User.t()) :: {:ok, User.t()}
1954   def perform(:delete, %User{} = user) do
1955     # Purge the user again, in case perform/2 is called directly
1956     purge(user)
1957
1958     # Remove all relationships
1959     user
1960     |> get_followers()
1961     |> Enum.each(fn follower ->
1962       ActivityPub.unfollow(follower, user)
1963       unfollow(follower, user)
1964     end)
1965
1966     user
1967     |> get_friends()
1968     |> Enum.each(fn followed ->
1969       ActivityPub.unfollow(user, followed)
1970       unfollow(user, followed)
1971     end)
1972
1973     delete_user_activities(user)
1974     delete_notifications_from_user_activities(user)
1975     delete_outgoing_pending_follow_requests(user)
1976
1977     maybe_delete_from_db(user)
1978   end
1979
1980   def perform(:set_activation_async, user, status), do: set_activation(user, status)
1981
1982   @spec external_users_query() :: Ecto.Query.t()
1983   def external_users_query do
1984     User.Query.build(%{
1985       external: true,
1986       active: true,
1987       order_by: :id
1988     })
1989   end
1990
1991   @spec external_users(keyword()) :: [User.t()]
1992   def external_users(opts \\ []) do
1993     query =
1994       external_users_query()
1995       |> select([u], struct(u, [:id, :ap_id]))
1996
1997     query =
1998       if opts[:max_id],
1999         do: where(query, [u], u.id > ^opts[:max_id]),
2000         else: query
2001
2002     query =
2003       if opts[:limit],
2004         do: limit(query, ^opts[:limit]),
2005         else: query
2006
2007     Repo.all(query)
2008   end
2009
2010   def delete_notifications_from_user_activities(%User{ap_id: ap_id}) do
2011     Notification
2012     |> join(:inner, [n], activity in assoc(n, :activity))
2013     |> where([n, a], fragment("? = ?", a.actor, ^ap_id))
2014     |> Repo.delete_all()
2015   end
2016
2017   def delete_user_activities(%User{ap_id: ap_id} = user) do
2018     ap_id
2019     |> Activity.Queries.by_actor()
2020     |> Repo.chunk_stream(50, :batches)
2021     |> Stream.each(fn activities ->
2022       Enum.each(activities, fn activity -> delete_activity(activity, user) end)
2023     end)
2024     |> Stream.run()
2025   end
2026
2027   defp delete_activity(%{data: %{"type" => "Create", "object" => object}} = activity, user) do
2028     with {_, %Object{}} <- {:find_object, Object.get_by_ap_id(object)},
2029          {:ok, delete_data, _} <- Builder.delete(user, object) do
2030       Pipeline.common_pipeline(delete_data, local: user.local)
2031     else
2032       {:find_object, nil} ->
2033         # We have the create activity, but not the object, it was probably pruned.
2034         # Insert a tombstone and try again
2035         with {:ok, tombstone_data, _} <- Builder.tombstone(user.ap_id, object),
2036              {:ok, _tombstone} <- Object.create(tombstone_data) do
2037           delete_activity(activity, user)
2038         end
2039
2040       e ->
2041         Logger.error("Could not delete #{object} created by #{activity.data["ap_id"]}")
2042         Logger.error("Error: #{inspect(e)}")
2043     end
2044   end
2045
2046   defp delete_activity(%{data: %{"type" => type}} = activity, user)
2047        when type in ["Like", "Announce"] do
2048     {:ok, undo, _} = Builder.undo(user, activity)
2049     Pipeline.common_pipeline(undo, local: user.local)
2050   end
2051
2052   defp delete_activity(_activity, _user), do: "Doing nothing"
2053
2054   defp delete_outgoing_pending_follow_requests(user) do
2055     user
2056     |> FollowingRelationship.outgoing_pending_follow_requests_query()
2057     |> Repo.delete_all()
2058   end
2059
2060   def html_filter_policy(%User{no_rich_text: true}) do
2061     Pleroma.HTML.Scrubber.TwitterText
2062   end
2063
2064   def html_filter_policy(_), do: Config.get([:markup, :scrub_policy])
2065
2066   def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
2067
2068   def get_or_fetch_by_ap_id(ap_id) do
2069     cached_user = get_cached_by_ap_id(ap_id)
2070
2071     maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id)
2072
2073     case {cached_user, maybe_fetched_user} do
2074       {_, {:ok, %User{} = user}} ->
2075         {:ok, user}
2076
2077       {%User{} = user, _} ->
2078         {:ok, user}
2079
2080       _ ->
2081         {:error, :not_found}
2082     end
2083   end
2084
2085   @doc """
2086   Creates an internal service actor by URI if missing.
2087   Optionally takes nickname for addressing.
2088   """
2089   @spec get_or_create_service_actor_by_ap_id(String.t(), String.t()) :: User.t() | nil
2090   def get_or_create_service_actor_by_ap_id(uri, nickname) do
2091     {_, user} =
2092       case get_cached_by_ap_id(uri) do
2093         nil ->
2094           with {:error, %{errors: errors}} <- create_service_actor(uri, nickname) do
2095             Logger.error("Cannot create service actor: #{uri}/.\n#{inspect(errors)}")
2096             {:error, nil}
2097           end
2098
2099         %User{invisible: false} = user ->
2100           set_invisible(user)
2101
2102         user ->
2103           {:ok, user}
2104       end
2105
2106     user
2107   end
2108
2109   @spec set_invisible(User.t()) :: {:ok, User.t()}
2110   defp set_invisible(user) do
2111     user
2112     |> change(%{invisible: true})
2113     |> update_and_set_cache()
2114   end
2115
2116   @spec create_service_actor(String.t(), String.t()) ::
2117           {:ok, User.t()} | {:error, Ecto.Changeset.t()}
2118   defp create_service_actor(uri, nickname) do
2119     %User{
2120       invisible: true,
2121       local: true,
2122       ap_id: uri,
2123       nickname: nickname,
2124       follower_address: uri <> "/followers"
2125     }
2126     |> change
2127     |> put_private_key()
2128     |> unique_constraint(:nickname)
2129     |> Repo.insert()
2130     |> set_cache()
2131   end
2132
2133   def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
2134     key =
2135       public_key_pem
2136       |> :public_key.pem_decode()
2137       |> hd()
2138       |> :public_key.pem_entry_decode()
2139
2140     {:ok, key}
2141   end
2142
2143   def public_key(_), do: {:error, "key not found"}
2144
2145   def get_public_key_for_ap_id(ap_id) do
2146     with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
2147          {:ok, public_key} <- public_key(user) do
2148       {:ok, public_key}
2149     else
2150       _ -> :error
2151     end
2152   end
2153
2154   def ap_enabled?(%User{local: true}), do: true
2155   def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
2156   def ap_enabled?(_), do: false
2157
2158   @doc "Gets or fetch a user by uri or nickname."
2159   @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
2160   def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
2161   def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
2162   def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
2163
2164   # wait a period of time and return newest version of the User structs
2165   # this is because we have synchronous follow APIs and need to simulate them
2166   # with an async handshake
2167   def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
2168     with %User{} = a <- get_cached_by_id(a.id),
2169          %User{} = b <- get_cached_by_id(b.id) do
2170       {:ok, a, b}
2171     else
2172       nil -> :error
2173     end
2174   end
2175
2176   def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
2177     with :ok <- :timer.sleep(timeout),
2178          %User{} = a <- get_cached_by_id(a.id),
2179          %User{} = b <- get_cached_by_id(b.id) do
2180       {:ok, a, b}
2181     else
2182       nil -> :error
2183     end
2184   end
2185
2186   def parse_bio(bio) when is_binary(bio) and bio != "" do
2187     bio
2188     |> CommonUtils.format_input("text/plain", mentions_format: :full)
2189     |> elem(0)
2190   end
2191
2192   def parse_bio(_), do: ""
2193
2194   def parse_bio(bio, user) when is_binary(bio) and bio != "" do
2195     # TODO: get profile URLs other than user.ap_id
2196     profile_urls = [user.ap_id]
2197
2198     bio
2199     |> CommonUtils.format_input("text/plain",
2200       mentions_format: :full,
2201       rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
2202     )
2203     |> elem(0)
2204   end
2205
2206   def parse_bio(_, _), do: ""
2207
2208   def tag(user_identifiers, tags) when is_list(user_identifiers) do
2209     Repo.transaction(fn ->
2210       for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
2211     end)
2212   end
2213
2214   def tag(nickname, tags) when is_binary(nickname),
2215     do: tag(get_by_nickname(nickname), tags)
2216
2217   def tag(%User{} = user, tags),
2218     do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
2219
2220   def untag(user_identifiers, tags) when is_list(user_identifiers) do
2221     Repo.transaction(fn ->
2222       for user_identifier <- user_identifiers, do: untag(user_identifier, tags)
2223     end)
2224   end
2225
2226   def untag(nickname, tags) when is_binary(nickname),
2227     do: untag(get_by_nickname(nickname), tags)
2228
2229   def untag(%User{} = user, tags),
2230     do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
2231
2232   defp update_tags(%User{} = user, new_tags) do
2233     {:ok, updated_user} =
2234       user
2235       |> change(%{tags: new_tags})
2236       |> update_and_set_cache()
2237
2238     updated_user
2239   end
2240
2241   defp normalize_tags(tags) do
2242     [tags]
2243     |> List.flatten()
2244     |> Enum.map(&String.downcase/1)
2245   end
2246
2247   defp local_nickname_regex do
2248     if Config.get([:instance, :extended_nickname_format]) do
2249       @extended_local_nickname_regex
2250     else
2251       @strict_local_nickname_regex
2252     end
2253   end
2254
2255   def local_nickname(nickname_or_mention) do
2256     nickname_or_mention
2257     |> full_nickname()
2258     |> String.split("@")
2259     |> hd()
2260   end
2261
2262   def full_nickname(%User{} = user) do
2263     if String.contains?(user.nickname, "@") do
2264       user.nickname
2265     else
2266       %{host: host} = URI.parse(user.ap_id)
2267       user.nickname <> "@" <> host
2268     end
2269   end
2270
2271   def full_nickname(nickname_or_mention),
2272     do: String.trim_leading(nickname_or_mention, "@")
2273
2274   def error_user(ap_id) do
2275     %User{
2276       name: ap_id,
2277       ap_id: ap_id,
2278       nickname: "erroruser@example.com",
2279       inserted_at: NaiveDateTime.utc_now()
2280     }
2281   end
2282
2283   @spec all_superusers() :: [User.t()]
2284   def all_superusers do
2285     User.Query.build(%{super_users: true, local: true, is_active: true})
2286     |> Repo.all()
2287   end
2288
2289   @spec all_users_with_privilege(atom()) :: [User.t()]
2290   def all_users_with_privilege(privilege) do
2291     User.Query.build(%{is_privileged: privilege}) |> Repo.all()
2292   end
2293
2294   def muting_reblogs?(%User{} = user, %User{} = target) do
2295     UserRelationship.reblog_mute_exists?(user, target)
2296   end
2297
2298   def showing_reblogs?(%User{} = user, %User{} = target) do
2299     not muting_reblogs?(user, target)
2300   end
2301
2302   @doc """
2303   The function returns a query to get users with no activity for given interval of days.
2304   Inactive users are those who didn't read any notification, or had any activity where
2305   the user is the activity's actor, during `inactivity_threshold` days.
2306   Deactivated users will not appear in this list.
2307
2308   ## Examples
2309
2310       iex> Pleroma.User.list_inactive_users()
2311       %Ecto.Query{}
2312   """
2313   @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
2314   def list_inactive_users_query(inactivity_threshold \\ 7) do
2315     negative_inactivity_threshold = -inactivity_threshold
2316     now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2317     # Subqueries are not supported in `where` clauses, join gets too complicated.
2318     has_read_notifications =
2319       from(n in Pleroma.Notification,
2320         where: n.seen == true,
2321         group_by: n.id,
2322         having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
2323         select: n.user_id
2324       )
2325       |> Pleroma.Repo.all()
2326
2327     from(u in Pleroma.User,
2328       left_join: a in Pleroma.Activity,
2329       on: u.ap_id == a.actor,
2330       where: not is_nil(u.nickname),
2331       where: u.is_active == ^true,
2332       where: u.id not in ^has_read_notifications,
2333       group_by: u.id,
2334       having:
2335         max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
2336           is_nil(max(a.inserted_at))
2337     )
2338   end
2339
2340   @doc """
2341   Enable or disable email notifications for user
2342
2343   ## Examples
2344
2345       iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => false}}, "digest", true)
2346       Pleroma.User{email_notifications: %{"digest" => true}}
2347
2348       iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => true}}, "digest", false)
2349       Pleroma.User{email_notifications: %{"digest" => false}}
2350   """
2351   @spec switch_email_notifications(t(), String.t(), boolean()) ::
2352           {:ok, t()} | {:error, Ecto.Changeset.t()}
2353   def switch_email_notifications(user, type, status) do
2354     User.update_email_notifications(user, %{type => status})
2355   end
2356
2357   @doc """
2358   Set `last_digest_emailed_at` value for the user to current time
2359   """
2360   @spec touch_last_digest_emailed_at(t()) :: t()
2361   def touch_last_digest_emailed_at(user) do
2362     now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2363
2364     {:ok, updated_user} =
2365       user
2366       |> change(%{last_digest_emailed_at: now})
2367       |> update_and_set_cache()
2368
2369     updated_user
2370   end
2371
2372   @spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
2373   def set_confirmation(%User{} = user, bool) do
2374     user
2375     |> confirmation_changeset(set_confirmation: bool)
2376     |> update_and_set_cache()
2377   end
2378
2379   def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
2380     mascot
2381   end
2382
2383   def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
2384     # use instance-default
2385     config = Config.get([:assets, :mascots])
2386     default_mascot = Config.get([:assets, :default_mascot])
2387     mascot = Keyword.get(config, default_mascot)
2388
2389     %{
2390       "id" => "default-mascot",
2391       "url" => mascot[:url],
2392       "preview_url" => mascot[:url],
2393       "pleroma" => %{
2394         "mime_type" => mascot[:mime_type]
2395       }
2396     }
2397   end
2398
2399   def get_ap_ids_by_nicknames(nicknames) do
2400     from(u in User,
2401       where: u.nickname in ^nicknames,
2402       order_by: fragment("array_position(?, ?)", ^nicknames, u.nickname),
2403       select: u.ap_id
2404     )
2405     |> Repo.all()
2406   end
2407
2408   defp put_password_hash(
2409          %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
2410        ) do
2411     change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
2412   end
2413
2414   defp put_password_hash(changeset), do: changeset
2415
2416   def is_internal_user?(%User{nickname: nil}), do: true
2417   def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true
2418   def is_internal_user?(_), do: false
2419
2420   # A hack because user delete activities have a fake id for whatever reason
2421   # TODO: Get rid of this
2422   def get_delivered_users_by_object_id("pleroma:fake_object_id"), do: []
2423
2424   def get_delivered_users_by_object_id(object_id) do
2425     from(u in User,
2426       inner_join: delivery in assoc(u, :deliveries),
2427       where: delivery.object_id == ^object_id
2428     )
2429     |> Repo.all()
2430   end
2431
2432   def change_email(user, email) do
2433     user
2434     |> cast(%{email: email}, [:email])
2435     |> maybe_validate_required_email(false)
2436     |> unique_constraint(:email)
2437     |> validate_format(:email, @email_regex)
2438     |> update_and_set_cache()
2439   end
2440
2441   def alias_users(user) do
2442     user.also_known_as
2443     |> Enum.map(&User.get_cached_by_ap_id/1)
2444     |> Enum.filter(fn user -> user != nil end)
2445   end
2446
2447   def add_alias(user, new_alias_user) do
2448     current_aliases = user.also_known_as || []
2449     new_alias_ap_id = new_alias_user.ap_id
2450
2451     if new_alias_ap_id in current_aliases do
2452       {:ok, user}
2453     else
2454       user
2455       |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as])
2456       |> update_and_set_cache()
2457     end
2458   end
2459
2460   def delete_alias(user, alias_user) do
2461     current_aliases = user.also_known_as || []
2462     alias_ap_id = alias_user.ap_id
2463
2464     if alias_ap_id in current_aliases do
2465       user
2466       |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as])
2467       |> update_and_set_cache()
2468     else
2469       {:error, :no_such_alias}
2470     end
2471   end
2472
2473   # Internal function; public one is `deactivate/2`
2474   defp set_activation_status(user, status) do
2475     user
2476     |> cast(%{is_active: status}, [:is_active])
2477     |> update_and_set_cache()
2478   end
2479
2480   def update_banner(user, banner) do
2481     user
2482     |> cast(%{banner: banner}, [:banner])
2483     |> update_and_set_cache()
2484   end
2485
2486   def update_background(user, background) do
2487     user
2488     |> cast(%{background: background}, [:background])
2489     |> update_and_set_cache()
2490   end
2491
2492   def validate_fields(changeset, remote? \\ false) do
2493     limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
2494     limit = Config.get([:instance, limit_name], 0)
2495
2496     changeset
2497     |> validate_length(:fields, max: limit)
2498     |> validate_change(:fields, fn :fields, fields ->
2499       if Enum.all?(fields, &valid_field?/1) do
2500         []
2501       else
2502         [fields: "invalid"]
2503       end
2504     end)
2505   end
2506
2507   defp valid_field?(%{"name" => name, "value" => value}) do
2508     name_limit = Config.get([:instance, :account_field_name_length], 255)
2509     value_limit = Config.get([:instance, :account_field_value_length], 255)
2510
2511     is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
2512       String.length(value) <= value_limit
2513   end
2514
2515   defp valid_field?(_), do: false
2516
2517   defp truncate_field(%{"name" => name, "value" => value}) do
2518     {name, _chopped} =
2519       String.split_at(name, Config.get([:instance, :account_field_name_length], 255))
2520
2521     {value, _chopped} =
2522       String.split_at(value, Config.get([:instance, :account_field_value_length], 255))
2523
2524     %{"name" => name, "value" => value}
2525   end
2526
2527   def admin_api_update(user, params) do
2528     user
2529     |> cast(params, [
2530       :is_moderator,
2531       :is_admin,
2532       :show_role
2533     ])
2534     |> update_and_set_cache()
2535   end
2536
2537   @doc "Signs user out of all applications"
2538   def global_sign_out(user) do
2539     OAuth.Authorization.delete_user_authorizations(user)
2540     OAuth.Token.delete_user_tokens(user)
2541   end
2542
2543   def mascot_update(user, url) do
2544     user
2545     |> cast(%{mascot: url}, [:mascot])
2546     |> validate_required([:mascot])
2547     |> update_and_set_cache()
2548   end
2549
2550   @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t()
2551   def confirmation_changeset(user, set_confirmation: confirmed?) do
2552     params =
2553       if confirmed? do
2554         %{
2555           is_confirmed: true,
2556           confirmation_token: nil
2557         }
2558       else
2559         %{
2560           is_confirmed: false,
2561           confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
2562         }
2563       end
2564
2565     cast(user, params, [:is_confirmed, :confirmation_token])
2566   end
2567
2568   @spec approval_changeset(User.t(), keyword()) :: Changeset.t()
2569   def approval_changeset(user, set_approval: approved?) do
2570     cast(user, %{is_approved: approved?}, [:is_approved])
2571   end
2572
2573   @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
2574   def add_pinned_object_id(%User{} = user, object_id) do
2575     if !user.pinned_objects[object_id] do
2576       params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())}
2577
2578       user
2579       |> cast(params, [:pinned_objects])
2580       |> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects ->
2581         max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
2582
2583         if Enum.count(pinned_objects) <= max_pinned_statuses do
2584           []
2585         else
2586           [pinned_objects: "You have already pinned the maximum number of statuses"]
2587         end
2588       end)
2589     else
2590       change(user)
2591     end
2592     |> update_and_set_cache()
2593   end
2594
2595   @spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()}
2596   def remove_pinned_object_id(%User{} = user, object_id) do
2597     user
2598     |> cast(
2599       %{pinned_objects: Map.delete(user.pinned_objects, object_id)},
2600       [:pinned_objects]
2601     )
2602     |> update_and_set_cache()
2603   end
2604
2605   def update_email_notifications(user, settings) do
2606     email_notifications =
2607       user.email_notifications
2608       |> Map.merge(settings)
2609       |> Map.take(["digest"])
2610
2611     params = %{email_notifications: email_notifications}
2612     fields = [:email_notifications]
2613
2614     user
2615     |> cast(params, fields)
2616     |> validate_required(fields)
2617     |> update_and_set_cache()
2618   end
2619
2620   defp set_domain_blocks(user, domain_blocks) do
2621     params = %{domain_blocks: domain_blocks}
2622
2623     user
2624     |> cast(params, [:domain_blocks])
2625     |> validate_required([:domain_blocks])
2626     |> update_and_set_cache()
2627   end
2628
2629   def block_domain(user, domain_blocked) do
2630     set_domain_blocks(user, Enum.uniq([domain_blocked | user.domain_blocks]))
2631   end
2632
2633   def unblock_domain(user, domain_blocked) do
2634     set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
2635   end
2636
2637   @spec add_to_block(User.t(), User.t()) ::
2638           {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
2639   defp add_to_block(%User{} = user, %User{} = blocked) do
2640     with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do
2641       @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
2642       {:ok, relationship}
2643     end
2644   end
2645
2646   @spec add_to_block(User.t(), User.t()) ::
2647           {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
2648   defp remove_from_block(%User{} = user, %User{} = blocked) do
2649     with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do
2650       @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
2651       {:ok, relationship}
2652     end
2653   end
2654
2655   def set_invisible(user, invisible) do
2656     params = %{invisible: invisible}
2657
2658     user
2659     |> cast(params, [:invisible])
2660     |> validate_required([:invisible])
2661     |> update_and_set_cache()
2662   end
2663
2664   def sanitize_html(%User{} = user) do
2665     sanitize_html(user, nil)
2666   end
2667
2668   # User data that mastodon isn't filtering (treated as plaintext):
2669   # - field name
2670   # - display name
2671   def sanitize_html(%User{} = user, filter) do
2672     fields =
2673       Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
2674         %{
2675           "name" => name,
2676           "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
2677         }
2678       end)
2679
2680     user
2681     |> Map.put(:bio, HTML.filter_tags(user.bio, filter))
2682     |> Map.put(:fields, fields)
2683   end
2684
2685   def get_host(%User{ap_id: ap_id} = _user) do
2686     URI.parse(ap_id).host
2687   end
2688
2689   def update_last_active_at(%__MODULE__{local: true} = user) do
2690     user
2691     |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at])
2692     |> update_and_set_cache()
2693   end
2694
2695   def active_user_count(days \\ 30) do
2696     active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
2697
2698     __MODULE__
2699     |> where([u], u.last_active_at >= ^active_after)
2700     |> where([u], u.local == true)
2701     |> Repo.aggregate(:count)
2702   end
2703
2704   def update_last_status_at(user) do
2705     User
2706     |> where(id: ^user.id)
2707     |> update([u], set: [last_status_at: fragment("NOW()")])
2708     |> select([u], u)
2709     |> Repo.update_all([])
2710     |> case do
2711       {1, [user]} -> set_cache(user)
2712       _ -> {:error, user}
2713     end
2714   end
2715
2716   def get_friends_birthdays_query(%User{} = user, day, month) do
2717     User.Query.build(%{
2718       friends: user,
2719       deactivated: false,
2720       birthday_day: day,
2721       birthday_month: month
2722     })
2723   end
2724 end