Merge branch 'develop' into feature/new-registrations-digest
[akkoma] / lib / pleroma / web / activity_pub / activity_pub.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.ActivityPub do
6 alias Pleroma.Activity
7 alias Pleroma.Activity.Ir.Topics
8 alias Pleroma.Config
9 alias Pleroma.Conversation
10 alias Pleroma.Conversation.Participation
11 alias Pleroma.Notification
12 alias Pleroma.Object
13 alias Pleroma.Object.Containment
14 alias Pleroma.Object.Fetcher
15 alias Pleroma.Pagination
16 alias Pleroma.Repo
17 alias Pleroma.Upload
18 alias Pleroma.User
19 alias Pleroma.Web.ActivityPub.MRF
20 alias Pleroma.Web.ActivityPub.Transmogrifier
21 alias Pleroma.Web.ActivityPub.Utils
22 alias Pleroma.Web.Streamer
23 alias Pleroma.Web.WebFinger
24 alias Pleroma.Workers.BackgroundWorker
25
26 import Ecto.Query
27 import Pleroma.Web.ActivityPub.Utils
28 import Pleroma.Web.ActivityPub.Visibility
29
30 require Logger
31 require Pleroma.Constants
32
33 # For Announce activities, we filter the recipients based on following status for any actors
34 # that match actual users. See issue #164 for more information about why this is necessary.
35 defp get_recipients(%{"type" => "Announce"} = data) do
36 to = Map.get(data, "to", [])
37 cc = Map.get(data, "cc", [])
38 bcc = Map.get(data, "bcc", [])
39 actor = User.get_cached_by_ap_id(data["actor"])
40
41 recipients =
42 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
43 case User.get_cached_by_ap_id(recipient) do
44 nil -> true
45 user -> User.following?(user, actor)
46 end
47 end)
48
49 {recipients, to, cc}
50 end
51
52 defp get_recipients(%{"type" => "Create"} = data) do
53 to = Map.get(data, "to", [])
54 cc = Map.get(data, "cc", [])
55 bcc = Map.get(data, "bcc", [])
56 actor = Map.get(data, "actor", [])
57 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
58 {recipients, to, cc}
59 end
60
61 defp get_recipients(data) do
62 to = Map.get(data, "to", [])
63 cc = Map.get(data, "cc", [])
64 bcc = Map.get(data, "bcc", [])
65 recipients = Enum.concat([to, cc, bcc])
66 {recipients, to, cc}
67 end
68
69 defp check_actor_is_active(actor) do
70 if not is_nil(actor) do
71 with user <- User.get_cached_by_ap_id(actor),
72 false <- user.deactivated do
73 true
74 else
75 _e -> false
76 end
77 else
78 true
79 end
80 end
81
82 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
83 limit = Config.get([:instance, :remote_limit])
84 String.length(content) <= limit
85 end
86
87 defp check_remote_limit(_), do: true
88
89 def increase_note_count_if_public(actor, object) do
90 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
91 end
92
93 def decrease_note_count_if_public(actor, object) do
94 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
95 end
96
97 def increase_replies_count_if_reply(%{
98 "object" => %{"inReplyTo" => reply_ap_id} = object,
99 "type" => "Create"
100 }) do
101 if is_public?(object) do
102 Object.increase_replies_count(reply_ap_id)
103 end
104 end
105
106 def increase_replies_count_if_reply(_create_data), do: :noop
107
108 def decrease_replies_count_if_reply(%Object{
109 data: %{"inReplyTo" => reply_ap_id} = object
110 }) do
111 if is_public?(object) do
112 Object.decrease_replies_count(reply_ap_id)
113 end
114 end
115
116 def decrease_replies_count_if_reply(_object), do: :noop
117
118 def increase_poll_votes_if_vote(%{
119 "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
120 "type" => "Create"
121 }) do
122 Object.increase_vote_count(reply_ap_id, name)
123 end
124
125 def increase_poll_votes_if_vote(_create_data), do: :noop
126
127 def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
128 with nil <- Activity.normalize(map),
129 map <- lazy_put_activity_defaults(map, fake),
130 true <- bypass_actor_check || check_actor_is_active(map["actor"]),
131 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
132 {:ok, map} <- MRF.filter(map),
133 {recipients, _, _} = get_recipients(map),
134 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
135 {:containment, :ok} <- {:containment, Containment.contain_child(map)},
136 {:ok, map, object} <- insert_full_object(map) do
137 {:ok, activity} =
138 Repo.insert(%Activity{
139 data: map,
140 local: local,
141 actor: map["actor"],
142 recipients: recipients
143 })
144
145 # Splice in the child object if we have one.
146 activity =
147 if not is_nil(object) do
148 Map.put(activity, :object, object)
149 else
150 activity
151 end
152
153 BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
154
155 Notification.create_notifications(activity)
156
157 conversation = create_or_bump_conversation(activity, map["actor"])
158 participations = get_participations(conversation)
159 stream_out(activity)
160 stream_out_participations(participations)
161 {:ok, activity}
162 else
163 %Activity{} = activity ->
164 {:ok, activity}
165
166 {:fake, true, map, recipients} ->
167 activity = %Activity{
168 data: map,
169 local: local,
170 actor: map["actor"],
171 recipients: recipients,
172 id: "pleroma:fakeid"
173 }
174
175 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
176 {:ok, activity}
177
178 error ->
179 {:error, error}
180 end
181 end
182
183 defp create_or_bump_conversation(activity, actor) do
184 with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
185 %User{} = user <- User.get_cached_by_ap_id(actor),
186 Participation.mark_as_read(user, conversation) do
187 {:ok, conversation}
188 end
189 end
190
191 defp get_participations({:ok, conversation}) do
192 conversation
193 |> Repo.preload(:participations, force: true)
194 |> Map.get(:participations)
195 end
196
197 defp get_participations(_), do: []
198
199 def stream_out_participations(participations) do
200 participations =
201 participations
202 |> Repo.preload(:user)
203
204 Streamer.stream("participation", participations)
205 end
206
207 def stream_out_participations(%Object{data: %{"context" => context}}, user) do
208 with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
209 conversation = Repo.preload(conversation, :participations),
210 last_activity_id =
211 fetch_latest_activity_id_for_context(conversation.ap_id, %{
212 "user" => user,
213 "blocking_user" => user
214 }) do
215 if last_activity_id do
216 stream_out_participations(conversation.participations)
217 end
218 end
219 end
220
221 def stream_out_participations(_, _), do: :noop
222
223 def stream_out(%Activity{data: %{"type" => data_type}} = activity)
224 when data_type in ["Create", "Announce", "Delete"] do
225 activity
226 |> Topics.get_activity_topics()
227 |> Streamer.stream(activity)
228 end
229
230 def stream_out(_activity) do
231 :noop
232 end
233
234 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
235 additional = params[:additional] || %{}
236 # only accept false as false value
237 local = !(params[:local] == false)
238 published = params[:published]
239 quick_insert? = Pleroma.Config.get([:env]) == :benchmark
240
241 with create_data <-
242 make_create_data(
243 %{to: to, actor: actor, published: published, context: context, object: object},
244 additional
245 ),
246 {:ok, activity} <- insert(create_data, local, fake),
247 {:fake, false, activity} <- {:fake, fake, activity},
248 _ <- increase_replies_count_if_reply(create_data),
249 _ <- increase_poll_votes_if_vote(create_data),
250 {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
251 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
252 :ok <- maybe_federate(activity) do
253 {:ok, activity}
254 else
255 {:quick_insert, true, activity} ->
256 {:ok, activity}
257
258 {:fake, true, activity} ->
259 {:ok, activity}
260
261 {:error, message} ->
262 {:error, message}
263 end
264 end
265
266 def listen(%{to: to, actor: actor, context: context, object: object} = params) do
267 additional = params[:additional] || %{}
268 # only accept false as false value
269 local = !(params[:local] == false)
270 published = params[:published]
271
272 with listen_data <-
273 make_listen_data(
274 %{to: to, actor: actor, published: published, context: context, object: object},
275 additional
276 ),
277 {:ok, activity} <- insert(listen_data, local),
278 :ok <- maybe_federate(activity) do
279 {:ok, activity}
280 else
281 {:error, message} ->
282 {:error, message}
283 end
284 end
285
286 def accept(params) do
287 accept_or_reject("Accept", params)
288 end
289
290 def reject(params) do
291 accept_or_reject("Reject", params)
292 end
293
294 def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
295 local = Map.get(params, :local, true)
296 activity_id = Map.get(params, :activity_id, nil)
297
298 with data <-
299 %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
300 |> Utils.maybe_put("id", activity_id),
301 {:ok, activity} <- insert(data, local),
302 :ok <- maybe_federate(activity) do
303 {:ok, activity}
304 end
305 end
306
307 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
308 local = !(params[:local] == false)
309 activity_id = params[:activity_id]
310
311 with data <- %{
312 "to" => to,
313 "cc" => cc,
314 "type" => "Update",
315 "actor" => actor,
316 "object" => object
317 },
318 data <- Utils.maybe_put(data, "id", activity_id),
319 {:ok, activity} <- insert(data, local),
320 :ok <- maybe_federate(activity) do
321 {:ok, activity}
322 end
323 end
324
325 def react_with_emoji(user, object, emoji, options \\ []) do
326 with local <- Keyword.get(options, :local, true),
327 activity_id <- Keyword.get(options, :activity_id, nil),
328 true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
329 reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
330 {:ok, activity} <- insert(reaction_data, local),
331 {:ok, object} <- add_emoji_reaction_to_object(activity, object),
332 :ok <- maybe_federate(activity) do
333 {:ok, activity, object}
334 else
335 e -> {:error, e}
336 end
337 end
338
339 def unreact_with_emoji(user, reaction_id, options \\ []) do
340 with local <- Keyword.get(options, :local, true),
341 activity_id <- Keyword.get(options, :activity_id, nil),
342 user_ap_id <- user.ap_id,
343 %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
344 object <- Object.normalize(reaction_activity),
345 unreact_data <- make_undo_data(user, reaction_activity, activity_id),
346 {:ok, activity} <- insert(unreact_data, local),
347 {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
348 :ok <- maybe_federate(activity) do
349 {:ok, activity, object}
350 else
351 e -> {:error, e}
352 end
353 end
354
355 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
356 def like(
357 %User{ap_id: ap_id} = user,
358 %Object{data: %{"id" => _}} = object,
359 activity_id \\ nil,
360 local \\ true
361 ) do
362 with nil <- get_existing_like(ap_id, object),
363 like_data <- make_like_data(user, object, activity_id),
364 {:ok, activity} <- insert(like_data, local),
365 {:ok, object} <- add_like_to_object(activity, object),
366 :ok <- maybe_federate(activity) do
367 {:ok, activity, object}
368 else
369 %Activity{} = activity -> {:ok, activity, object}
370 error -> {:error, error}
371 end
372 end
373
374 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
375 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
376 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
377 {:ok, unlike_activity} <- insert(unlike_data, local),
378 {:ok, _activity} <- Repo.delete(like_activity),
379 {:ok, object} <- remove_like_from_object(like_activity, object),
380 :ok <- maybe_federate(unlike_activity) do
381 {:ok, unlike_activity, like_activity, object}
382 else
383 _e -> {:ok, object}
384 end
385 end
386
387 def announce(
388 %User{ap_id: _} = user,
389 %Object{data: %{"id" => _}} = object,
390 activity_id \\ nil,
391 local \\ true,
392 public \\ true
393 ) do
394 with true <- is_announceable?(object, user, public),
395 announce_data <- make_announce_data(user, object, activity_id, public),
396 {:ok, activity} <- insert(announce_data, local),
397 {:ok, object} <- add_announce_to_object(activity, object),
398 :ok <- maybe_federate(activity) do
399 {:ok, activity, object}
400 else
401 error -> {:error, error}
402 end
403 end
404
405 def unannounce(
406 %User{} = actor,
407 %Object{} = object,
408 activity_id \\ nil,
409 local \\ true
410 ) do
411 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
412 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
413 {:ok, unannounce_activity} <- insert(unannounce_data, local),
414 :ok <- maybe_federate(unannounce_activity),
415 {:ok, _activity} <- Repo.delete(announce_activity),
416 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
417 {:ok, unannounce_activity, object}
418 else
419 _e -> {:ok, object}
420 end
421 end
422
423 def follow(follower, followed, activity_id \\ nil, local \\ true) do
424 with data <- make_follow_data(follower, followed, activity_id),
425 {:ok, activity} <- insert(data, local),
426 :ok <- maybe_federate(activity),
427 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
428 {:ok, activity}
429 end
430 end
431
432 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
433 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
434 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
435 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
436 {:ok, activity} <- insert(unfollow_data, local),
437 :ok <- maybe_federate(activity) do
438 {:ok, activity}
439 end
440 end
441
442 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
443 with data <- %{
444 "to" => [follower_address],
445 "type" => "Delete",
446 "actor" => ap_id,
447 "object" => %{"type" => "Person", "id" => ap_id}
448 },
449 {:ok, activity} <- insert(data, true, true, true),
450 :ok <- maybe_federate(activity) do
451 {:ok, user}
452 end
453 end
454
455 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
456 local = Keyword.get(options, :local, true)
457 activity_id = Keyword.get(options, :activity_id, nil)
458 actor = Keyword.get(options, :actor, actor)
459
460 user = User.get_cached_by_ap_id(actor)
461 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
462
463 with create_activity <- Activity.get_create_by_object_ap_id(id),
464 data <-
465 %{
466 "type" => "Delete",
467 "actor" => actor,
468 "object" => id,
469 "to" => to,
470 "deleted_activity_id" => create_activity && create_activity.id
471 }
472 |> maybe_put("id", activity_id),
473 {:ok, activity} <- insert(data, local, false),
474 {:ok, object, _create_activity} <- Object.delete(object),
475 stream_out_participations(object, user),
476 _ <- decrease_replies_count_if_reply(object),
477 {:ok, _actor} <- decrease_note_count_if_public(user, object),
478 :ok <- maybe_federate(activity) do
479 {:ok, activity}
480 end
481 end
482
483 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
484 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
485 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
486 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
487
488 if unfollow_blocked do
489 follow_activity = fetch_latest_follow(blocker, blocked)
490 if follow_activity, do: unfollow(blocker, blocked, nil, local)
491 end
492
493 with true <- outgoing_blocks,
494 block_data <- make_block_data(blocker, blocked, activity_id),
495 {:ok, activity} <- insert(block_data, local),
496 :ok <- maybe_federate(activity) do
497 {:ok, activity}
498 else
499 _e -> {:ok, nil}
500 end
501 end
502
503 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
504 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
505 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
506 {:ok, activity} <- insert(unblock_data, local),
507 :ok <- maybe_federate(activity) do
508 {:ok, activity}
509 end
510 end
511
512 @spec flag(map()) :: {:ok, Activity.t()} | any
513 def flag(
514 %{
515 actor: actor,
516 context: _context,
517 account: account,
518 statuses: statuses,
519 content: content
520 } = params
521 ) do
522 # only accept false as false value
523 local = !(params[:local] == false)
524 forward = !(params[:forward] == false)
525
526 additional = params[:additional] || %{}
527
528 additional =
529 if forward do
530 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
531 else
532 Map.merge(additional, %{"to" => [], "cc" => []})
533 end
534
535 with flag_data <- make_flag_data(params, additional),
536 {:ok, activity} <- insert(flag_data, local),
537 {:ok, stripped_activity} <- strip_report_status_data(activity),
538 :ok <- maybe_federate(stripped_activity) do
539 Enum.each(User.all_superusers(), fn superuser ->
540 superuser
541 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
542 |> Pleroma.Emails.Mailer.deliver_async()
543 end)
544
545 {:ok, activity}
546 end
547 end
548
549 def move(%User{} = origin, %User{} = target, local \\ true) do
550 params = %{
551 "type" => "Move",
552 "actor" => origin.ap_id,
553 "object" => origin.ap_id,
554 "target" => target.ap_id
555 }
556
557 with true <- origin.ap_id in target.also_known_as,
558 {:ok, activity} <- insert(params, local) do
559 maybe_federate(activity)
560
561 BackgroundWorker.enqueue("move_following", %{
562 "origin_id" => origin.id,
563 "target_id" => target.id
564 })
565
566 {:ok, activity}
567 else
568 false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
569 err -> err
570 end
571 end
572
573 defp fetch_activities_for_context_query(context, opts) do
574 public = [Pleroma.Constants.as_public()]
575
576 recipients =
577 if opts["user"],
578 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
579 else: public
580
581 from(activity in Activity)
582 |> maybe_preload_objects(opts)
583 |> maybe_preload_bookmarks(opts)
584 |> maybe_set_thread_muted_field(opts)
585 |> restrict_blocked(opts)
586 |> restrict_recipients(recipients, opts["user"])
587 |> where(
588 [activity],
589 fragment(
590 "?->>'type' = ? and ?->>'context' = ?",
591 activity.data,
592 "Create",
593 activity.data,
594 ^context
595 )
596 )
597 |> exclude_poll_votes(opts)
598 |> exclude_id(opts)
599 |> order_by([activity], desc: activity.id)
600 end
601
602 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
603 def fetch_activities_for_context(context, opts \\ %{}) do
604 context
605 |> fetch_activities_for_context_query(opts)
606 |> Repo.all()
607 end
608
609 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
610 FlakeId.Ecto.CompatType.t() | nil
611 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
612 context
613 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
614 |> limit(1)
615 |> select([a], a.id)
616 |> Repo.one()
617 end
618
619 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
620 opts = Map.drop(opts, ["user"])
621
622 [Pleroma.Constants.as_public()]
623 |> fetch_activities_query(opts)
624 |> restrict_unlisted()
625 |> Pagination.fetch_paginated(opts, pagination)
626 end
627
628 @valid_visibilities ~w[direct unlisted public private]
629
630 defp restrict_visibility(query, %{visibility: visibility})
631 when is_list(visibility) do
632 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
633 query =
634 from(
635 a in query,
636 where:
637 fragment(
638 "activity_visibility(?, ?, ?) = ANY (?)",
639 a.actor,
640 a.recipients,
641 a.data,
642 ^visibility
643 )
644 )
645
646 query
647 else
648 Logger.error("Could not restrict visibility to #{visibility}")
649 end
650 end
651
652 defp restrict_visibility(query, %{visibility: visibility})
653 when visibility in @valid_visibilities do
654 from(
655 a in query,
656 where:
657 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
658 )
659 end
660
661 defp restrict_visibility(_query, %{visibility: visibility})
662 when visibility not in @valid_visibilities do
663 Logger.error("Could not restrict visibility to #{visibility}")
664 end
665
666 defp restrict_visibility(query, _visibility), do: query
667
668 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
669 when is_list(visibility) do
670 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
671 from(
672 a in query,
673 where:
674 not fragment(
675 "activity_visibility(?, ?, ?) = ANY (?)",
676 a.actor,
677 a.recipients,
678 a.data,
679 ^visibility
680 )
681 )
682 else
683 Logger.error("Could not exclude visibility to #{visibility}")
684 query
685 end
686 end
687
688 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
689 when visibility in @valid_visibilities do
690 from(
691 a in query,
692 where:
693 not fragment(
694 "activity_visibility(?, ?, ?) = ?",
695 a.actor,
696 a.recipients,
697 a.data,
698 ^visibility
699 )
700 )
701 end
702
703 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
704 when visibility not in @valid_visibilities do
705 Logger.error("Could not exclude visibility to #{visibility}")
706 query
707 end
708
709 defp exclude_visibility(query, _visibility), do: query
710
711 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
712 do: query
713
714 defp restrict_thread_visibility(
715 query,
716 %{"user" => %User{skip_thread_containment: true}},
717 _
718 ),
719 do: query
720
721 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
722 from(
723 a in query,
724 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
725 )
726 end
727
728 defp restrict_thread_visibility(query, _, _), do: query
729
730 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
731 params =
732 params
733 |> Map.put("user", reading_user)
734 |> Map.put("actor_id", user.ap_id)
735
736 recipients =
737 user_activities_recipients(%{
738 "godmode" => params["godmode"],
739 "reading_user" => reading_user
740 })
741
742 fetch_activities(recipients, params)
743 |> Enum.reverse()
744 end
745
746 def fetch_user_activities(user, reading_user, params \\ %{}) do
747 params =
748 params
749 |> Map.put("type", ["Create", "Announce"])
750 |> Map.put("user", reading_user)
751 |> Map.put("actor_id", user.ap_id)
752 |> Map.put("pinned_activity_ids", user.pinned_activities)
753
754 params =
755 if User.blocks?(reading_user, user) do
756 params
757 else
758 params
759 |> Map.put("blocking_user", reading_user)
760 |> Map.put("muting_user", reading_user)
761 end
762
763 recipients =
764 user_activities_recipients(%{
765 "godmode" => params["godmode"],
766 "reading_user" => reading_user
767 })
768
769 fetch_activities(recipients, params)
770 |> Enum.reverse()
771 end
772
773 def fetch_instance_activities(params) do
774 params =
775 params
776 |> Map.put("type", ["Create", "Announce"])
777 |> Map.put("instance", params["instance"])
778
779 fetch_activities([Pleroma.Constants.as_public()], params, :offset)
780 |> Enum.reverse()
781 end
782
783 defp user_activities_recipients(%{"godmode" => true}) do
784 []
785 end
786
787 defp user_activities_recipients(%{"reading_user" => reading_user}) do
788 if reading_user do
789 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
790 else
791 [Pleroma.Constants.as_public()]
792 end
793 end
794
795 defp restrict_since(query, %{"since_id" => ""}), do: query
796
797 defp restrict_since(query, %{"since_id" => since_id}) do
798 from(activity in query, where: activity.id > ^since_id)
799 end
800
801 defp restrict_since(query, _), do: query
802
803 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
804 raise "Can't use the child object without preloading!"
805 end
806
807 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
808 when is_list(tag_reject) and tag_reject != [] do
809 from(
810 [_activity, object] in query,
811 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
812 )
813 end
814
815 defp restrict_tag_reject(query, _), do: query
816
817 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
818 raise "Can't use the child object without preloading!"
819 end
820
821 defp restrict_tag_all(query, %{"tag_all" => tag_all})
822 when is_list(tag_all) and tag_all != [] do
823 from(
824 [_activity, object] in query,
825 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
826 )
827 end
828
829 defp restrict_tag_all(query, _), do: query
830
831 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
832 raise "Can't use the child object without preloading!"
833 end
834
835 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
836 from(
837 [_activity, object] in query,
838 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
839 )
840 end
841
842 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
843 from(
844 [_activity, object] in query,
845 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
846 )
847 end
848
849 defp restrict_tag(query, _), do: query
850
851 defp restrict_recipients(query, [], _user), do: query
852
853 defp restrict_recipients(query, recipients, nil) do
854 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
855 end
856
857 defp restrict_recipients(query, recipients, user) do
858 from(
859 activity in query,
860 where: fragment("? && ?", ^recipients, activity.recipients),
861 or_where: activity.actor == ^user.ap_id
862 )
863 end
864
865 defp restrict_local(query, %{"local_only" => true}) do
866 from(activity in query, where: activity.local == true)
867 end
868
869 defp restrict_local(query, _), do: query
870
871 defp restrict_actor(query, %{"actor_id" => actor_id}) do
872 from(activity in query, where: activity.actor == ^actor_id)
873 end
874
875 defp restrict_actor(query, _), do: query
876
877 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
878 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
879 end
880
881 defp restrict_type(query, %{"type" => type}) do
882 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
883 end
884
885 defp restrict_type(query, _), do: query
886
887 defp restrict_state(query, %{"state" => state}) do
888 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
889 end
890
891 defp restrict_state(query, _), do: query
892
893 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
894 from(
895 [_activity, object] in query,
896 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
897 )
898 end
899
900 defp restrict_favorited_by(query, _), do: query
901
902 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
903 raise "Can't use the child object without preloading!"
904 end
905
906 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
907 from(
908 [_activity, object] in query,
909 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
910 )
911 end
912
913 defp restrict_media(query, _), do: query
914
915 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
916 from(
917 [_activity, object] in query,
918 where: fragment("?->>'inReplyTo' is null", object.data)
919 )
920 end
921
922 defp restrict_replies(query, _), do: query
923
924 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
925 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
926 end
927
928 defp restrict_reblogs(query, _), do: query
929
930 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
931
932 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
933 mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
934
935 query =
936 from([activity] in query,
937 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
938 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
939 )
940
941 unless opts["skip_preload"] do
942 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
943 else
944 query
945 end
946 end
947
948 defp restrict_muted(query, _), do: query
949
950 defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
951 blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
952 domain_blocks = user.domain_blocks || []
953
954 following_ap_ids = User.get_friends_ap_ids(user)
955
956 query =
957 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
958
959 from(
960 [activity, object: o] in query,
961 where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
962 where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
963 where:
964 fragment(
965 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
966 activity.data,
967 activity.data,
968 ^blocked_ap_ids
969 ),
970 where:
971 fragment(
972 "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
973 activity.actor,
974 ^domain_blocks,
975 activity.actor,
976 ^following_ap_ids
977 ),
978 where:
979 fragment(
980 "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
981 o.data,
982 ^domain_blocks,
983 o.data,
984 ^following_ap_ids
985 )
986 )
987 end
988
989 defp restrict_blocked(query, _), do: query
990
991 defp restrict_unlisted(query) do
992 from(
993 activity in query,
994 where:
995 fragment(
996 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
997 activity.data,
998 ^[Pleroma.Constants.as_public()]
999 )
1000 )
1001 end
1002
1003 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
1004 from(activity in query, where: activity.id in ^ids)
1005 end
1006
1007 defp restrict_pinned(query, _), do: query
1008
1009 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
1010 muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
1011
1012 from(
1013 activity in query,
1014 where:
1015 fragment(
1016 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
1017 activity.data,
1018 activity.actor,
1019 ^muted_reblogs
1020 )
1021 )
1022 end
1023
1024 defp restrict_muted_reblogs(query, _), do: query
1025
1026 defp restrict_instance(query, %{"instance" => instance}) do
1027 users =
1028 from(
1029 u in User,
1030 select: u.ap_id,
1031 where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
1032 )
1033 |> Repo.all()
1034
1035 from(activity in query, where: activity.actor in ^users)
1036 end
1037
1038 defp restrict_instance(query, _), do: query
1039
1040 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
1041
1042 defp exclude_poll_votes(query, _) do
1043 if has_named_binding?(query, :object) do
1044 from([activity, object: o] in query,
1045 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
1046 )
1047 else
1048 query
1049 end
1050 end
1051
1052 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
1053 from(activity in query, where: activity.id != ^id)
1054 end
1055
1056 defp exclude_id(query, _), do: query
1057
1058 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
1059
1060 defp maybe_preload_objects(query, _) do
1061 query
1062 |> Activity.with_preloaded_object()
1063 end
1064
1065 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
1066
1067 defp maybe_preload_bookmarks(query, opts) do
1068 query
1069 |> Activity.with_preloaded_bookmark(opts["user"])
1070 end
1071
1072 defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
1073 query
1074 |> Activity.with_preloaded_report_notes()
1075 end
1076
1077 defp maybe_preload_report_notes(query, _), do: query
1078
1079 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
1080
1081 defp maybe_set_thread_muted_field(query, opts) do
1082 query
1083 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
1084 end
1085
1086 defp maybe_order(query, %{order: :desc}) do
1087 query
1088 |> order_by(desc: :id)
1089 end
1090
1091 defp maybe_order(query, %{order: :asc}) do
1092 query
1093 |> order_by(asc: :id)
1094 end
1095
1096 defp maybe_order(query, _), do: query
1097
1098 defp fetch_activities_query_ap_ids_ops(opts) do
1099 source_user = opts["muting_user"]
1100 ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
1101
1102 ap_id_relations =
1103 ap_id_relations ++
1104 if opts["blocking_user"] && opts["blocking_user"] == source_user do
1105 [:block]
1106 else
1107 []
1108 end
1109
1110 preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
1111
1112 restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
1113 restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
1114
1115 restrict_muted_reblogs_opts =
1116 Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
1117
1118 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
1119 end
1120
1121 def fetch_activities_query(recipients, opts \\ %{}) do
1122 {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
1123 fetch_activities_query_ap_ids_ops(opts)
1124
1125 config = %{
1126 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
1127 }
1128
1129 Activity
1130 |> maybe_preload_objects(opts)
1131 |> maybe_preload_bookmarks(opts)
1132 |> maybe_preload_report_notes(opts)
1133 |> maybe_set_thread_muted_field(opts)
1134 |> maybe_order(opts)
1135 |> restrict_recipients(recipients, opts["user"])
1136 |> restrict_tag(opts)
1137 |> restrict_tag_reject(opts)
1138 |> restrict_tag_all(opts)
1139 |> restrict_since(opts)
1140 |> restrict_local(opts)
1141 |> restrict_actor(opts)
1142 |> restrict_type(opts)
1143 |> restrict_state(opts)
1144 |> restrict_favorited_by(opts)
1145 |> restrict_blocked(restrict_blocked_opts)
1146 |> restrict_muted(restrict_muted_opts)
1147 |> restrict_media(opts)
1148 |> restrict_visibility(opts)
1149 |> restrict_thread_visibility(opts, config)
1150 |> restrict_replies(opts)
1151 |> restrict_reblogs(opts)
1152 |> restrict_pinned(opts)
1153 |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
1154 |> restrict_instance(opts)
1155 |> Activity.restrict_deactivated_users()
1156 |> exclude_poll_votes(opts)
1157 |> exclude_visibility(opts)
1158 end
1159
1160 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1161 list_memberships = Pleroma.List.memberships(opts["user"])
1162
1163 fetch_activities_query(recipients ++ list_memberships, opts)
1164 |> Pagination.fetch_paginated(opts, pagination)
1165 |> Enum.reverse()
1166 |> maybe_update_cc(list_memberships, opts["user"])
1167 end
1168
1169 @doc """
1170 Fetch favorites activities of user with order by sort adds to favorites
1171 """
1172 @spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
1173 def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
1174 user.ap_id
1175 |> Activity.Queries.by_actor()
1176 |> Activity.Queries.by_type("Like")
1177 |> Activity.with_joined_object()
1178 |> Object.with_joined_activity()
1179 |> select([_like, object, activity], %{activity | object: object})
1180 |> order_by([like, _, _], desc: like.id)
1181 |> Pagination.fetch_paginated(
1182 Map.merge(params, %{"skip_order" => true}),
1183 pagination,
1184 :object_activity
1185 )
1186 end
1187
1188 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1189 when is_list(list_memberships) and length(list_memberships) > 0 do
1190 Enum.map(activities, fn
1191 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1192 if Enum.any?(bcc, &(&1 in list_memberships)) do
1193 update_in(activity.data["cc"], &[user_ap_id | &1])
1194 else
1195 activity
1196 end
1197
1198 activity ->
1199 activity
1200 end)
1201 end
1202
1203 defp maybe_update_cc(activities, _, _), do: activities
1204
1205 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1206 from(activity in query,
1207 where:
1208 fragment("? && ?", activity.recipients, ^recipients) or
1209 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1210 ^Pleroma.Constants.as_public() in activity.recipients)
1211 )
1212 end
1213
1214 def fetch_activities_bounded(
1215 recipients,
1216 recipients_with_public,
1217 opts \\ %{},
1218 pagination \\ :keyset
1219 ) do
1220 fetch_activities_query([], opts)
1221 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1222 |> Pagination.fetch_paginated(opts, pagination)
1223 |> Enum.reverse()
1224 end
1225
1226 def upload(file, opts \\ []) do
1227 with {:ok, data} <- Upload.store(file, opts) do
1228 obj_data =
1229 if opts[:actor] do
1230 Map.put(data, "actor", opts[:actor])
1231 else
1232 data
1233 end
1234
1235 Repo.insert(%Object{data: obj_data})
1236 end
1237 end
1238
1239 defp object_to_user_data(data) do
1240 avatar =
1241 data["icon"]["url"] &&
1242 %{
1243 "type" => "Image",
1244 "url" => [%{"href" => data["icon"]["url"]}]
1245 }
1246
1247 banner =
1248 data["image"]["url"] &&
1249 %{
1250 "type" => "Image",
1251 "url" => [%{"href" => data["image"]["url"]}]
1252 }
1253
1254 fields =
1255 data
1256 |> Map.get("attachment", [])
1257 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1258 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1259
1260 locked = data["manuallyApprovesFollowers"] || false
1261 data = Transmogrifier.maybe_fix_user_object(data)
1262 discoverable = data["discoverable"] || false
1263 invisible = data["invisible"] || false
1264 actor_type = data["type"] || "Person"
1265
1266 user_data = %{
1267 ap_id: data["id"],
1268 ap_enabled: true,
1269 source_data: data,
1270 banner: banner,
1271 fields: fields,
1272 locked: locked,
1273 discoverable: discoverable,
1274 invisible: invisible,
1275 avatar: avatar,
1276 name: data["name"],
1277 follower_address: data["followers"],
1278 following_address: data["following"],
1279 bio: data["summary"],
1280 actor_type: actor_type,
1281 also_known_as: Map.get(data, "alsoKnownAs", [])
1282 }
1283
1284 # nickname can be nil because of virtual actors
1285 user_data =
1286 if data["preferredUsername"] do
1287 Map.put(
1288 user_data,
1289 :nickname,
1290 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1291 )
1292 else
1293 Map.put(user_data, :nickname, nil)
1294 end
1295
1296 {:ok, user_data}
1297 end
1298
1299 def fetch_follow_information_for_user(user) do
1300 with {:ok, following_data} <-
1301 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1302 {:ok, hide_follows} <- collection_private(following_data),
1303 {:ok, followers_data} <-
1304 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1305 {:ok, hide_followers} <- collection_private(followers_data) do
1306 {:ok,
1307 %{
1308 hide_follows: hide_follows,
1309 follower_count: normalize_counter(followers_data["totalItems"]),
1310 following_count: normalize_counter(following_data["totalItems"]),
1311 hide_followers: hide_followers
1312 }}
1313 else
1314 {:error, _} = e -> e
1315 e -> {:error, e}
1316 end
1317 end
1318
1319 defp normalize_counter(counter) when is_integer(counter), do: counter
1320 defp normalize_counter(_), do: 0
1321
1322 defp maybe_update_follow_information(data) do
1323 with {:enabled, true} <-
1324 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1325 {:ok, info} <- fetch_follow_information_for_user(data) do
1326 info = Map.merge(data[:info] || %{}, info)
1327 Map.put(data, :info, info)
1328 else
1329 {:enabled, false} ->
1330 data
1331
1332 e ->
1333 Logger.error(
1334 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1335 )
1336
1337 data
1338 end
1339 end
1340
1341 defp collection_private(%{"first" => %{"type" => type}})
1342 when type in ["CollectionPage", "OrderedCollectionPage"],
1343 do: {:ok, false}
1344
1345 defp collection_private(%{"first" => first}) do
1346 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1347 Fetcher.fetch_and_contain_remote_object_from_id(first) do
1348 {:ok, false}
1349 else
1350 {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
1351 {:error, _} = e -> e
1352 e -> {:error, e}
1353 end
1354 end
1355
1356 defp collection_private(_data), do: {:ok, true}
1357
1358 def user_data_from_user_object(data) do
1359 with {:ok, data} <- MRF.filter(data),
1360 {:ok, data} <- object_to_user_data(data) do
1361 {:ok, data}
1362 else
1363 e -> {:error, e}
1364 end
1365 end
1366
1367 def fetch_and_prepare_user_from_ap_id(ap_id) do
1368 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1369 {:ok, data} <- user_data_from_user_object(data),
1370 data <- maybe_update_follow_information(data) do
1371 {:ok, data}
1372 else
1373 {:error, "Object has been deleted"} = e ->
1374 Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1375 {:error, e}
1376
1377 e ->
1378 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1379 {:error, e}
1380 end
1381 end
1382
1383 def make_user_from_ap_id(ap_id) do
1384 if _user = User.get_cached_by_ap_id(ap_id) do
1385 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1386 else
1387 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1388 User.insert_or_update_user(data)
1389 else
1390 e -> {:error, e}
1391 end
1392 end
1393 end
1394
1395 def make_user_from_nickname(nickname) do
1396 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1397 make_user_from_ap_id(ap_id)
1398 else
1399 _e -> {:error, "No AP id in WebFinger"}
1400 end
1401 end
1402
1403 # filter out broken threads
1404 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1405 entire_thread_visible_for_user?(activity, user)
1406 end
1407
1408 # do post-processing on a specific activity
1409 def contain_activity(%Activity{} = activity, %User{} = user) do
1410 contain_broken_threads(activity, user)
1411 end
1412
1413 def fetch_direct_messages_query do
1414 Activity
1415 |> restrict_type(%{"type" => "Create"})
1416 |> restrict_visibility(%{visibility: "direct"})
1417 |> order_by([activity], asc: activity.id)
1418 end
1419 end