Merge remote-tracking branch 'remotes/upstream/develop' into 1304-user-info-deprecation
[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 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
326 def like(
327 %User{ap_id: ap_id} = user,
328 %Object{data: %{"id" => _}} = object,
329 activity_id \\ nil,
330 local \\ true
331 ) do
332 with nil <- get_existing_like(ap_id, object),
333 like_data <- make_like_data(user, object, activity_id),
334 {:ok, activity} <- insert(like_data, local),
335 {:ok, object} <- add_like_to_object(activity, object),
336 :ok <- maybe_federate(activity) do
337 {:ok, activity, object}
338 else
339 %Activity{} = activity -> {:ok, activity, object}
340 error -> {:error, error}
341 end
342 end
343
344 def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
345 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
346 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
347 {:ok, unlike_activity} <- insert(unlike_data, local),
348 {:ok, _activity} <- Repo.delete(like_activity),
349 {:ok, object} <- remove_like_from_object(like_activity, object),
350 :ok <- maybe_federate(unlike_activity) do
351 {:ok, unlike_activity, like_activity, object}
352 else
353 _e -> {:ok, object}
354 end
355 end
356
357 def announce(
358 %User{ap_id: _} = user,
359 %Object{data: %{"id" => _}} = object,
360 activity_id \\ nil,
361 local \\ true,
362 public \\ true
363 ) do
364 with true <- is_announceable?(object, user, public),
365 announce_data <- make_announce_data(user, object, activity_id, public),
366 {:ok, activity} <- insert(announce_data, local),
367 {:ok, object} <- add_announce_to_object(activity, object),
368 :ok <- maybe_federate(activity) do
369 {:ok, activity, object}
370 else
371 error -> {:error, error}
372 end
373 end
374
375 def unannounce(
376 %User{} = actor,
377 %Object{} = object,
378 activity_id \\ nil,
379 local \\ true
380 ) do
381 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
382 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
383 {:ok, unannounce_activity} <- insert(unannounce_data, local),
384 :ok <- maybe_federate(unannounce_activity),
385 {:ok, _activity} <- Repo.delete(announce_activity),
386 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
387 {:ok, unannounce_activity, object}
388 else
389 _e -> {:ok, object}
390 end
391 end
392
393 def follow(follower, followed, activity_id \\ nil, local \\ true) do
394 with data <- make_follow_data(follower, followed, activity_id),
395 {:ok, activity} <- insert(data, local),
396 :ok <- maybe_federate(activity),
397 _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
398 {:ok, activity}
399 end
400 end
401
402 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
403 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
404 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
405 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
406 {:ok, activity} <- insert(unfollow_data, local),
407 :ok <- maybe_federate(activity) do
408 {:ok, activity}
409 end
410 end
411
412 def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
413 with data <- %{
414 "to" => [follower_address],
415 "type" => "Delete",
416 "actor" => ap_id,
417 "object" => %{"type" => "Person", "id" => ap_id}
418 },
419 {:ok, activity} <- insert(data, true, true, true),
420 :ok <- maybe_federate(activity) do
421 {:ok, user}
422 end
423 end
424
425 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
426 local = Keyword.get(options, :local, true)
427 activity_id = Keyword.get(options, :activity_id, nil)
428 actor = Keyword.get(options, :actor, actor)
429
430 user = User.get_cached_by_ap_id(actor)
431 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
432
433 with {:ok, object, activity} <- Object.delete(object),
434 data <-
435 %{
436 "type" => "Delete",
437 "actor" => actor,
438 "object" => id,
439 "to" => to,
440 "deleted_activity_id" => activity && activity.id
441 }
442 |> maybe_put("id", activity_id),
443 {:ok, activity} <- insert(data, local, false),
444 stream_out_participations(object, user),
445 _ <- decrease_replies_count_if_reply(object),
446 {:ok, _actor} <- decrease_note_count_if_public(user, object),
447 :ok <- maybe_federate(activity) do
448 {:ok, activity}
449 end
450 end
451
452 @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
453 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
454 outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
455 unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
456
457 if unfollow_blocked do
458 follow_activity = fetch_latest_follow(blocker, blocked)
459 if follow_activity, do: unfollow(blocker, blocked, nil, local)
460 end
461
462 with true <- outgoing_blocks,
463 block_data <- make_block_data(blocker, blocked, activity_id),
464 {:ok, activity} <- insert(block_data, local),
465 :ok <- maybe_federate(activity) do
466 {:ok, activity}
467 else
468 _e -> {:ok, nil}
469 end
470 end
471
472 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
473 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
474 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
475 {:ok, activity} <- insert(unblock_data, local),
476 :ok <- maybe_federate(activity) do
477 {:ok, activity}
478 end
479 end
480
481 @spec flag(map()) :: {:ok, Activity.t()} | any
482 def flag(
483 %{
484 actor: actor,
485 context: _context,
486 account: account,
487 statuses: statuses,
488 content: content
489 } = params
490 ) do
491 # only accept false as false value
492 local = !(params[:local] == false)
493 forward = !(params[:forward] == false)
494
495 additional = params[:additional] || %{}
496
497 additional =
498 if forward do
499 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
500 else
501 Map.merge(additional, %{"to" => [], "cc" => []})
502 end
503
504 with flag_data <- make_flag_data(params, additional),
505 {:ok, activity} <- insert(flag_data, local),
506 :ok <- maybe_federate(activity) do
507 Enum.each(User.all_superusers(), fn superuser ->
508 superuser
509 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
510 |> Pleroma.Emails.Mailer.deliver_async()
511 end)
512
513 {:ok, activity}
514 end
515 end
516
517 defp fetch_activities_for_context_query(context, opts) do
518 public = [Pleroma.Constants.as_public()]
519
520 recipients =
521 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
522
523 from(activity in Activity)
524 |> maybe_preload_objects(opts)
525 |> maybe_preload_bookmarks(opts)
526 |> maybe_set_thread_muted_field(opts)
527 |> restrict_blocked(opts)
528 |> restrict_recipients(recipients, opts["user"])
529 |> where(
530 [activity],
531 fragment(
532 "?->>'type' = ? and ?->>'context' = ?",
533 activity.data,
534 "Create",
535 activity.data,
536 ^context
537 )
538 )
539 |> exclude_poll_votes(opts)
540 |> exclude_id(opts)
541 |> order_by([activity], desc: activity.id)
542 end
543
544 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
545 def fetch_activities_for_context(context, opts \\ %{}) do
546 context
547 |> fetch_activities_for_context_query(opts)
548 |> Repo.all()
549 end
550
551 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
552 FlakeId.Ecto.CompatType.t() | nil
553 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
554 context
555 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
556 |> limit(1)
557 |> select([a], a.id)
558 |> Repo.one()
559 end
560
561 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
562 opts = Map.drop(opts, ["user"])
563
564 [Pleroma.Constants.as_public()]
565 |> fetch_activities_query(opts)
566 |> restrict_unlisted()
567 |> Pagination.fetch_paginated(opts, pagination)
568 |> Enum.reverse()
569 end
570
571 @valid_visibilities ~w[direct unlisted public private]
572
573 defp restrict_visibility(query, %{visibility: visibility})
574 when is_list(visibility) do
575 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
576 query =
577 from(
578 a in query,
579 where:
580 fragment(
581 "activity_visibility(?, ?, ?) = ANY (?)",
582 a.actor,
583 a.recipients,
584 a.data,
585 ^visibility
586 )
587 )
588
589 query
590 else
591 Logger.error("Could not restrict visibility to #{visibility}")
592 end
593 end
594
595 defp restrict_visibility(query, %{visibility: visibility})
596 when visibility in @valid_visibilities do
597 from(
598 a in query,
599 where:
600 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
601 )
602 end
603
604 defp restrict_visibility(_query, %{visibility: visibility})
605 when visibility not in @valid_visibilities do
606 Logger.error("Could not restrict visibility to #{visibility}")
607 end
608
609 defp restrict_visibility(query, _visibility), do: query
610
611 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
612 when is_list(visibility) do
613 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
614 from(
615 a in query,
616 where:
617 not fragment(
618 "activity_visibility(?, ?, ?) = ANY (?)",
619 a.actor,
620 a.recipients,
621 a.data,
622 ^visibility
623 )
624 )
625 else
626 Logger.error("Could not exclude visibility to #{visibility}")
627 query
628 end
629 end
630
631 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
632 when visibility in @valid_visibilities do
633 from(
634 a in query,
635 where:
636 not fragment(
637 "activity_visibility(?, ?, ?) = ?",
638 a.actor,
639 a.recipients,
640 a.data,
641 ^visibility
642 )
643 )
644 end
645
646 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
647 when visibility not in @valid_visibilities do
648 Logger.error("Could not exclude visibility to #{visibility}")
649 query
650 end
651
652 defp exclude_visibility(query, _visibility), do: query
653
654 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
655 do: query
656
657 defp restrict_thread_visibility(
658 query,
659 %{"user" => %User{skip_thread_containment: true}},
660 _
661 ),
662 do: query
663
664 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
665 from(
666 a in query,
667 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
668 )
669 end
670
671 defp restrict_thread_visibility(query, _, _), do: query
672
673 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
674 params =
675 params
676 |> Map.put("user", reading_user)
677 |> Map.put("actor_id", user.ap_id)
678 |> Map.put("whole_db", true)
679
680 recipients =
681 user_activities_recipients(%{
682 "godmode" => params["godmode"],
683 "reading_user" => reading_user
684 })
685
686 fetch_activities(recipients, params)
687 |> Enum.reverse()
688 end
689
690 def fetch_user_activities(user, reading_user, params \\ %{}) do
691 params =
692 params
693 |> Map.put("type", ["Create", "Announce"])
694 |> Map.put("user", reading_user)
695 |> Map.put("actor_id", user.ap_id)
696 |> Map.put("whole_db", true)
697 |> Map.put("pinned_activity_ids", user.pinned_activities)
698
699 recipients =
700 user_activities_recipients(%{
701 "godmode" => params["godmode"],
702 "reading_user" => reading_user
703 })
704
705 fetch_activities(recipients, params)
706 |> Enum.reverse()
707 end
708
709 defp user_activities_recipients(%{"godmode" => true}) do
710 []
711 end
712
713 defp user_activities_recipients(%{"reading_user" => reading_user}) do
714 if reading_user do
715 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
716 else
717 [Pleroma.Constants.as_public()]
718 end
719 end
720
721 defp restrict_since(query, %{"since_id" => ""}), do: query
722
723 defp restrict_since(query, %{"since_id" => since_id}) do
724 from(activity in query, where: activity.id > ^since_id)
725 end
726
727 defp restrict_since(query, _), do: query
728
729 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
730 raise "Can't use the child object without preloading!"
731 end
732
733 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
734 when is_list(tag_reject) and tag_reject != [] do
735 from(
736 [_activity, object] in query,
737 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
738 )
739 end
740
741 defp restrict_tag_reject(query, _), do: query
742
743 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
744 raise "Can't use the child object without preloading!"
745 end
746
747 defp restrict_tag_all(query, %{"tag_all" => tag_all})
748 when is_list(tag_all) and tag_all != [] do
749 from(
750 [_activity, object] in query,
751 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
752 )
753 end
754
755 defp restrict_tag_all(query, _), do: query
756
757 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
758 raise "Can't use the child object without preloading!"
759 end
760
761 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
762 from(
763 [_activity, object] in query,
764 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
765 )
766 end
767
768 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
769 from(
770 [_activity, object] in query,
771 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
772 )
773 end
774
775 defp restrict_tag(query, _), do: query
776
777 defp restrict_recipients(query, [], _user), do: query
778
779 defp restrict_recipients(query, recipients, nil) do
780 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
781 end
782
783 defp restrict_recipients(query, recipients, user) do
784 from(
785 activity in query,
786 where: fragment("? && ?", ^recipients, activity.recipients),
787 or_where: activity.actor == ^user.ap_id
788 )
789 end
790
791 defp restrict_local(query, %{"local_only" => true}) do
792 from(activity in query, where: activity.local == true)
793 end
794
795 defp restrict_local(query, _), do: query
796
797 defp restrict_actor(query, %{"actor_id" => actor_id}) do
798 from(activity in query, where: activity.actor == ^actor_id)
799 end
800
801 defp restrict_actor(query, _), do: query
802
803 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
804 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
805 end
806
807 defp restrict_type(query, %{"type" => type}) do
808 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
809 end
810
811 defp restrict_type(query, _), do: query
812
813 defp restrict_state(query, %{"state" => state}) do
814 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
815 end
816
817 defp restrict_state(query, _), do: query
818
819 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
820 from(
821 [_activity, object] in query,
822 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
823 )
824 end
825
826 defp restrict_favorited_by(query, _), do: query
827
828 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
829 raise "Can't use the child object without preloading!"
830 end
831
832 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
833 from(
834 [_activity, object] in query,
835 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
836 )
837 end
838
839 defp restrict_media(query, _), do: query
840
841 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
842 from(
843 [_activity, object] in query,
844 where: fragment("?->>'inReplyTo' is null", object.data)
845 )
846 end
847
848 defp restrict_replies(query, _), do: query
849
850 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
851 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
852 end
853
854 defp restrict_reblogs(query, _), do: query
855
856 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
857
858 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
859 mutes = user.mutes
860
861 query =
862 from([activity] in query,
863 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
864 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
865 )
866
867 unless opts["skip_preload"] do
868 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
869 else
870 query
871 end
872 end
873
874 defp restrict_muted(query, _), do: query
875
876 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
877 blocks = user.blocks || []
878 domain_blocks = user.domain_blocks || []
879
880 query =
881 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
882
883 from(
884 [activity, object: o] in query,
885 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
886 where: fragment("not (? && ?)", activity.recipients, ^blocks),
887 where:
888 fragment(
889 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
890 activity.data,
891 activity.data,
892 ^blocks
893 ),
894 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
895 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
896 )
897 end
898
899 defp restrict_blocked(query, _), do: query
900
901 defp restrict_unlisted(query) do
902 from(
903 activity in query,
904 where:
905 fragment(
906 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
907 activity.data,
908 ^[Pleroma.Constants.as_public()]
909 )
910 )
911 end
912
913 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
914 from(activity in query, where: activity.id in ^ids)
915 end
916
917 defp restrict_pinned(query, _), do: query
918
919 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
920 muted_reblogs = user.muted_reblogs || []
921
922 from(
923 activity in query,
924 where:
925 fragment(
926 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
927 activity.data,
928 activity.actor,
929 ^muted_reblogs
930 )
931 )
932 end
933
934 defp restrict_muted_reblogs(query, _), do: query
935
936 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
937
938 defp exclude_poll_votes(query, _) do
939 if has_named_binding?(query, :object) do
940 from([activity, object: o] in query,
941 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
942 )
943 else
944 query
945 end
946 end
947
948 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
949 from(activity in query, where: activity.id != ^id)
950 end
951
952 defp exclude_id(query, _), do: query
953
954 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
955
956 defp maybe_preload_objects(query, _) do
957 query
958 |> Activity.with_preloaded_object()
959 end
960
961 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
962
963 defp maybe_preload_bookmarks(query, opts) do
964 query
965 |> Activity.with_preloaded_bookmark(opts["user"])
966 end
967
968 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
969
970 defp maybe_set_thread_muted_field(query, opts) do
971 query
972 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
973 end
974
975 defp maybe_order(query, %{order: :desc}) do
976 query
977 |> order_by(desc: :id)
978 end
979
980 defp maybe_order(query, %{order: :asc}) do
981 query
982 |> order_by(asc: :id)
983 end
984
985 defp maybe_order(query, _), do: query
986
987 def fetch_activities_query(recipients, opts \\ %{}) do
988 config = %{
989 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
990 }
991
992 Activity
993 |> maybe_preload_objects(opts)
994 |> maybe_preload_bookmarks(opts)
995 |> maybe_set_thread_muted_field(opts)
996 |> maybe_order(opts)
997 |> restrict_recipients(recipients, opts["user"])
998 |> restrict_tag(opts)
999 |> restrict_tag_reject(opts)
1000 |> restrict_tag_all(opts)
1001 |> restrict_since(opts)
1002 |> restrict_local(opts)
1003 |> restrict_actor(opts)
1004 |> restrict_type(opts)
1005 |> restrict_state(opts)
1006 |> restrict_favorited_by(opts)
1007 |> restrict_blocked(opts)
1008 |> restrict_muted(opts)
1009 |> restrict_media(opts)
1010 |> restrict_visibility(opts)
1011 |> restrict_thread_visibility(opts, config)
1012 |> restrict_replies(opts)
1013 |> restrict_reblogs(opts)
1014 |> restrict_pinned(opts)
1015 |> restrict_muted_reblogs(opts)
1016 |> Activity.restrict_deactivated_users()
1017 |> exclude_poll_votes(opts)
1018 |> exclude_visibility(opts)
1019 end
1020
1021 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1022 list_memberships = Pleroma.List.memberships(opts["user"])
1023
1024 fetch_activities_query(recipients ++ list_memberships, opts)
1025 |> Pagination.fetch_paginated(opts, pagination)
1026 |> Enum.reverse()
1027 |> maybe_update_cc(list_memberships, opts["user"])
1028 end
1029
1030 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1031 when is_list(list_memberships) and length(list_memberships) > 0 do
1032 Enum.map(activities, fn
1033 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1034 if Enum.any?(bcc, &(&1 in list_memberships)) do
1035 update_in(activity.data["cc"], &[user_ap_id | &1])
1036 else
1037 activity
1038 end
1039
1040 activity ->
1041 activity
1042 end)
1043 end
1044
1045 defp maybe_update_cc(activities, _, _), do: activities
1046
1047 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1048 from(activity in query,
1049 where:
1050 fragment("? && ?", activity.recipients, ^recipients) or
1051 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1052 ^Pleroma.Constants.as_public() in activity.recipients)
1053 )
1054 end
1055
1056 def fetch_activities_bounded(
1057 recipients,
1058 recipients_with_public,
1059 opts \\ %{},
1060 pagination \\ :keyset
1061 ) do
1062 fetch_activities_query([], opts)
1063 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1064 |> Pagination.fetch_paginated(opts, pagination)
1065 |> Enum.reverse()
1066 end
1067
1068 def upload(file, opts \\ []) do
1069 with {:ok, data} <- Upload.store(file, opts) do
1070 obj_data =
1071 if opts[:actor] do
1072 Map.put(data, "actor", opts[:actor])
1073 else
1074 data
1075 end
1076
1077 Repo.insert(%Object{data: obj_data})
1078 end
1079 end
1080
1081 defp object_to_user_data(data) do
1082 avatar =
1083 data["icon"]["url"] &&
1084 %{
1085 "type" => "Image",
1086 "url" => [%{"href" => data["icon"]["url"]}]
1087 }
1088
1089 banner =
1090 data["image"]["url"] &&
1091 %{
1092 "type" => "Image",
1093 "url" => [%{"href" => data["image"]["url"]}]
1094 }
1095
1096 fields =
1097 data
1098 |> Map.get("attachment", [])
1099 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1100 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1101
1102 locked = data["manuallyApprovesFollowers"] || false
1103 data = Transmogrifier.maybe_fix_user_object(data)
1104 discoverable = data["discoverable"] || false
1105
1106 user_data = %{
1107 ap_id: data["id"],
1108 banner: banner,
1109 discoverable: discoverable,
1110 source_data: data,
1111 fields: fields,
1112 locked: locked,
1113 ap_enabled: true,
1114 avatar: avatar,
1115 name: data["name"],
1116 follower_address: data["followers"],
1117 following_address: data["following"],
1118 bio: data["summary"]
1119 }
1120
1121 # nickname can be nil because of virtual actors
1122 user_data =
1123 if data["preferredUsername"] do
1124 Map.put(
1125 user_data,
1126 :nickname,
1127 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1128 )
1129 else
1130 Map.put(user_data, :nickname, nil)
1131 end
1132
1133 {:ok, user_data}
1134 end
1135
1136 def fetch_follow_information_for_user(user) do
1137 with {:ok, following_data} <-
1138 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1139 following_count when is_integer(following_count) <- following_data["totalItems"],
1140 {:ok, hide_follows} <- collection_private(following_data),
1141 {:ok, followers_data} <-
1142 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1143 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1144 {:ok, hide_followers} <- collection_private(followers_data) do
1145 {:ok,
1146 %{
1147 hide_follows: hide_follows,
1148 follower_count: followers_count,
1149 following_count: following_count,
1150 hide_followers: hide_followers
1151 }}
1152 else
1153 {:error, _} = e ->
1154 e
1155
1156 e ->
1157 {:error, e}
1158 end
1159 end
1160
1161 defp maybe_update_follow_information(data) do
1162 with {:enabled, true} <-
1163 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1164 {:ok, info} <- fetch_follow_information_for_user(data) do
1165 info = Map.merge(data.info, info)
1166 Map.put(data, :info, info)
1167 else
1168 {:enabled, false} ->
1169 data
1170
1171 e ->
1172 Logger.error(
1173 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1174 )
1175
1176 data
1177 end
1178 end
1179
1180 defp collection_private(data) do
1181 if is_map(data["first"]) and
1182 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1183 {:ok, false}
1184 else
1185 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1186 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1187 {:ok, false}
1188 else
1189 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1190 {:ok, true}
1191
1192 {:error, _} = e ->
1193 e
1194
1195 e ->
1196 {:error, e}
1197 end
1198 end
1199 end
1200
1201 def user_data_from_user_object(data) do
1202 with {:ok, data} <- MRF.filter(data),
1203 {:ok, data} <- object_to_user_data(data) do
1204 {:ok, data}
1205 else
1206 e -> {:error, e}
1207 end
1208 end
1209
1210 def fetch_and_prepare_user_from_ap_id(ap_id) do
1211 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1212 {:ok, data} <- user_data_from_user_object(data),
1213 data <- maybe_update_follow_information(data) do
1214 {:ok, data}
1215 else
1216 e ->
1217 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1218 {:error, e}
1219 end
1220 end
1221
1222 def make_user_from_ap_id(ap_id) do
1223 if _user = User.get_cached_by_ap_id(ap_id) do
1224 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1225 else
1226 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1227 User.insert_or_update_user(data)
1228 else
1229 e -> {:error, e}
1230 end
1231 end
1232 end
1233
1234 def make_user_from_nickname(nickname) do
1235 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1236 make_user_from_ap_id(ap_id)
1237 else
1238 _e -> {:error, "No AP id in WebFinger"}
1239 end
1240 end
1241
1242 # filter out broken threads
1243 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1244 entire_thread_visible_for_user?(activity, user)
1245 end
1246
1247 # do post-processing on a specific activity
1248 def contain_activity(%Activity{} = activity, %User{} = user) do
1249 contain_broken_threads(activity, user)
1250 end
1251
1252 def fetch_direct_messages_query do
1253 Activity
1254 |> restrict_type(%{"type" => "Create"})
1255 |> restrict_visibility(%{visibility: "direct"})
1256 |> order_by([activity], asc: activity.id)
1257 end
1258 end