Merge branch 'refactor/db-add-defaults' into 'develop'
[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"],
522 do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
523 else: public
524
525 from(activity in Activity)
526 |> maybe_preload_objects(opts)
527 |> maybe_preload_bookmarks(opts)
528 |> maybe_set_thread_muted_field(opts)
529 |> restrict_blocked(opts)
530 |> restrict_recipients(recipients, opts["user"])
531 |> where(
532 [activity],
533 fragment(
534 "?->>'type' = ? and ?->>'context' = ?",
535 activity.data,
536 "Create",
537 activity.data,
538 ^context
539 )
540 )
541 |> exclude_poll_votes(opts)
542 |> exclude_id(opts)
543 |> order_by([activity], desc: activity.id)
544 end
545
546 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
547 def fetch_activities_for_context(context, opts \\ %{}) do
548 context
549 |> fetch_activities_for_context_query(opts)
550 |> Repo.all()
551 end
552
553 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
554 FlakeId.Ecto.CompatType.t() | nil
555 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
556 context
557 |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
558 |> limit(1)
559 |> select([a], a.id)
560 |> Repo.one()
561 end
562
563 def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
564 opts = Map.drop(opts, ["user"])
565
566 [Pleroma.Constants.as_public()]
567 |> fetch_activities_query(opts)
568 |> restrict_unlisted()
569 |> Pagination.fetch_paginated(opts, pagination)
570 |> Enum.reverse()
571 end
572
573 @valid_visibilities ~w[direct unlisted public private]
574
575 defp restrict_visibility(query, %{visibility: visibility})
576 when is_list(visibility) do
577 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
578 query =
579 from(
580 a in query,
581 where:
582 fragment(
583 "activity_visibility(?, ?, ?) = ANY (?)",
584 a.actor,
585 a.recipients,
586 a.data,
587 ^visibility
588 )
589 )
590
591 query
592 else
593 Logger.error("Could not restrict visibility to #{visibility}")
594 end
595 end
596
597 defp restrict_visibility(query, %{visibility: visibility})
598 when visibility in @valid_visibilities do
599 from(
600 a in query,
601 where:
602 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
603 )
604 end
605
606 defp restrict_visibility(_query, %{visibility: visibility})
607 when visibility not in @valid_visibilities do
608 Logger.error("Could not restrict visibility to #{visibility}")
609 end
610
611 defp restrict_visibility(query, _visibility), do: query
612
613 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
614 when is_list(visibility) do
615 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
616 from(
617 a in query,
618 where:
619 not fragment(
620 "activity_visibility(?, ?, ?) = ANY (?)",
621 a.actor,
622 a.recipients,
623 a.data,
624 ^visibility
625 )
626 )
627 else
628 Logger.error("Could not exclude visibility to #{visibility}")
629 query
630 end
631 end
632
633 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
634 when visibility in @valid_visibilities do
635 from(
636 a in query,
637 where:
638 not fragment(
639 "activity_visibility(?, ?, ?) = ?",
640 a.actor,
641 a.recipients,
642 a.data,
643 ^visibility
644 )
645 )
646 end
647
648 defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
649 when visibility not in @valid_visibilities do
650 Logger.error("Could not exclude visibility to #{visibility}")
651 query
652 end
653
654 defp exclude_visibility(query, _visibility), do: query
655
656 defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
657 do: query
658
659 defp restrict_thread_visibility(
660 query,
661 %{"user" => %User{skip_thread_containment: true}},
662 _
663 ),
664 do: query
665
666 defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
667 from(
668 a in query,
669 where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
670 )
671 end
672
673 defp restrict_thread_visibility(query, _, _), do: query
674
675 def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
676 params =
677 params
678 |> Map.put("user", reading_user)
679 |> Map.put("actor_id", user.ap_id)
680 |> Map.put("whole_db", true)
681
682 recipients =
683 user_activities_recipients(%{
684 "godmode" => params["godmode"],
685 "reading_user" => reading_user
686 })
687
688 fetch_activities(recipients, params)
689 |> Enum.reverse()
690 end
691
692 def fetch_user_activities(user, reading_user, params \\ %{}) do
693 params =
694 params
695 |> Map.put("type", ["Create", "Announce"])
696 |> Map.put("user", reading_user)
697 |> Map.put("actor_id", user.ap_id)
698 |> Map.put("whole_db", true)
699 |> Map.put("pinned_activity_ids", user.pinned_activities)
700
701 recipients =
702 user_activities_recipients(%{
703 "godmode" => params["godmode"],
704 "reading_user" => reading_user
705 })
706
707 fetch_activities(recipients, params)
708 |> Enum.reverse()
709 end
710
711 defp user_activities_recipients(%{"godmode" => true}) do
712 []
713 end
714
715 defp user_activities_recipients(%{"reading_user" => reading_user}) do
716 if reading_user do
717 [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
718 else
719 [Pleroma.Constants.as_public()]
720 end
721 end
722
723 defp restrict_since(query, %{"since_id" => ""}), do: query
724
725 defp restrict_since(query, %{"since_id" => since_id}) do
726 from(activity in query, where: activity.id > ^since_id)
727 end
728
729 defp restrict_since(query, _), do: query
730
731 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
732 raise "Can't use the child object without preloading!"
733 end
734
735 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
736 when is_list(tag_reject) and tag_reject != [] do
737 from(
738 [_activity, object] in query,
739 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
740 )
741 end
742
743 defp restrict_tag_reject(query, _), do: query
744
745 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
746 raise "Can't use the child object without preloading!"
747 end
748
749 defp restrict_tag_all(query, %{"tag_all" => tag_all})
750 when is_list(tag_all) and tag_all != [] do
751 from(
752 [_activity, object] in query,
753 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
754 )
755 end
756
757 defp restrict_tag_all(query, _), do: query
758
759 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
760 raise "Can't use the child object without preloading!"
761 end
762
763 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
764 from(
765 [_activity, object] in query,
766 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
767 )
768 end
769
770 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
771 from(
772 [_activity, object] in query,
773 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
774 )
775 end
776
777 defp restrict_tag(query, _), do: query
778
779 defp restrict_recipients(query, [], _user), do: query
780
781 defp restrict_recipients(query, recipients, nil) do
782 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
783 end
784
785 defp restrict_recipients(query, recipients, user) do
786 from(
787 activity in query,
788 where: fragment("? && ?", ^recipients, activity.recipients),
789 or_where: activity.actor == ^user.ap_id
790 )
791 end
792
793 defp restrict_local(query, %{"local_only" => true}) do
794 from(activity in query, where: activity.local == true)
795 end
796
797 defp restrict_local(query, _), do: query
798
799 defp restrict_actor(query, %{"actor_id" => actor_id}) do
800 from(activity in query, where: activity.actor == ^actor_id)
801 end
802
803 defp restrict_actor(query, _), do: query
804
805 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
806 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
807 end
808
809 defp restrict_type(query, %{"type" => type}) do
810 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
811 end
812
813 defp restrict_type(query, _), do: query
814
815 defp restrict_state(query, %{"state" => state}) do
816 from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
817 end
818
819 defp restrict_state(query, _), do: query
820
821 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
822 from(
823 [_activity, object] in query,
824 where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
825 )
826 end
827
828 defp restrict_favorited_by(query, _), do: query
829
830 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
831 raise "Can't use the child object without preloading!"
832 end
833
834 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
835 from(
836 [_activity, object] in query,
837 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
838 )
839 end
840
841 defp restrict_media(query, _), do: query
842
843 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
844 from(
845 [_activity, object] in query,
846 where: fragment("?->>'inReplyTo' is null", object.data)
847 )
848 end
849
850 defp restrict_replies(query, _), do: query
851
852 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
853 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
854 end
855
856 defp restrict_reblogs(query, _), do: query
857
858 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
859
860 defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
861 mutes = user.mutes
862
863 query =
864 from([activity] in query,
865 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
866 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
867 )
868
869 unless opts["skip_preload"] do
870 from([thread_mute: tm] in query, where: is_nil(tm.user_id))
871 else
872 query
873 end
874 end
875
876 defp restrict_muted(query, _), do: query
877
878 defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
879 blocks = user.blocks || []
880 domain_blocks = user.domain_blocks || []
881
882 query =
883 if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
884
885 from(
886 [activity, object: o] in query,
887 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
888 where: fragment("not (? && ?)", activity.recipients, ^blocks),
889 where:
890 fragment(
891 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
892 activity.data,
893 activity.data,
894 ^blocks
895 ),
896 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
897 where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
898 )
899 end
900
901 defp restrict_blocked(query, _), do: query
902
903 defp restrict_unlisted(query) do
904 from(
905 activity in query,
906 where:
907 fragment(
908 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
909 activity.data,
910 ^[Pleroma.Constants.as_public()]
911 )
912 )
913 end
914
915 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
916 from(activity in query, where: activity.id in ^ids)
917 end
918
919 defp restrict_pinned(query, _), do: query
920
921 defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
922 muted_reblogs = user.muted_reblogs || []
923
924 from(
925 activity in query,
926 where:
927 fragment(
928 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
929 activity.data,
930 activity.actor,
931 ^muted_reblogs
932 )
933 )
934 end
935
936 defp restrict_muted_reblogs(query, _), do: query
937
938 defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
939
940 defp exclude_poll_votes(query, _) do
941 if has_named_binding?(query, :object) do
942 from([activity, object: o] in query,
943 where: fragment("not(?->>'type' = ?)", o.data, "Answer")
944 )
945 else
946 query
947 end
948 end
949
950 defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
951 from(activity in query, where: activity.id != ^id)
952 end
953
954 defp exclude_id(query, _), do: query
955
956 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
957
958 defp maybe_preload_objects(query, _) do
959 query
960 |> Activity.with_preloaded_object()
961 end
962
963 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
964
965 defp maybe_preload_bookmarks(query, opts) do
966 query
967 |> Activity.with_preloaded_bookmark(opts["user"])
968 end
969
970 defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
971
972 defp maybe_set_thread_muted_field(query, opts) do
973 query
974 |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
975 end
976
977 defp maybe_order(query, %{order: :desc}) do
978 query
979 |> order_by(desc: :id)
980 end
981
982 defp maybe_order(query, %{order: :asc}) do
983 query
984 |> order_by(asc: :id)
985 end
986
987 defp maybe_order(query, _), do: query
988
989 def fetch_activities_query(recipients, opts \\ %{}) do
990 config = %{
991 skip_thread_containment: Config.get([:instance, :skip_thread_containment])
992 }
993
994 Activity
995 |> maybe_preload_objects(opts)
996 |> maybe_preload_bookmarks(opts)
997 |> maybe_set_thread_muted_field(opts)
998 |> maybe_order(opts)
999 |> restrict_recipients(recipients, opts["user"])
1000 |> restrict_tag(opts)
1001 |> restrict_tag_reject(opts)
1002 |> restrict_tag_all(opts)
1003 |> restrict_since(opts)
1004 |> restrict_local(opts)
1005 |> restrict_actor(opts)
1006 |> restrict_type(opts)
1007 |> restrict_state(opts)
1008 |> restrict_favorited_by(opts)
1009 |> restrict_blocked(opts)
1010 |> restrict_muted(opts)
1011 |> restrict_media(opts)
1012 |> restrict_visibility(opts)
1013 |> restrict_thread_visibility(opts, config)
1014 |> restrict_replies(opts)
1015 |> restrict_reblogs(opts)
1016 |> restrict_pinned(opts)
1017 |> restrict_muted_reblogs(opts)
1018 |> Activity.restrict_deactivated_users()
1019 |> exclude_poll_votes(opts)
1020 |> exclude_visibility(opts)
1021 end
1022
1023 def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
1024 list_memberships = Pleroma.List.memberships(opts["user"])
1025
1026 fetch_activities_query(recipients ++ list_memberships, opts)
1027 |> Pagination.fetch_paginated(opts, pagination)
1028 |> Enum.reverse()
1029 |> maybe_update_cc(list_memberships, opts["user"])
1030 end
1031
1032 defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
1033 when is_list(list_memberships) and length(list_memberships) > 0 do
1034 Enum.map(activities, fn
1035 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
1036 if Enum.any?(bcc, &(&1 in list_memberships)) do
1037 update_in(activity.data["cc"], &[user_ap_id | &1])
1038 else
1039 activity
1040 end
1041
1042 activity ->
1043 activity
1044 end)
1045 end
1046
1047 defp maybe_update_cc(activities, _, _), do: activities
1048
1049 def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
1050 from(activity in query,
1051 where:
1052 fragment("? && ?", activity.recipients, ^recipients) or
1053 (fragment("? && ?", activity.recipients, ^recipients_with_public) and
1054 ^Pleroma.Constants.as_public() in activity.recipients)
1055 )
1056 end
1057
1058 def fetch_activities_bounded(
1059 recipients,
1060 recipients_with_public,
1061 opts \\ %{},
1062 pagination \\ :keyset
1063 ) do
1064 fetch_activities_query([], opts)
1065 |> fetch_activities_bounded_query(recipients, recipients_with_public)
1066 |> Pagination.fetch_paginated(opts, pagination)
1067 |> Enum.reverse()
1068 end
1069
1070 def upload(file, opts \\ []) do
1071 with {:ok, data} <- Upload.store(file, opts) do
1072 obj_data =
1073 if opts[:actor] do
1074 Map.put(data, "actor", opts[:actor])
1075 else
1076 data
1077 end
1078
1079 Repo.insert(%Object{data: obj_data})
1080 end
1081 end
1082
1083 defp object_to_user_data(data) do
1084 avatar =
1085 data["icon"]["url"] &&
1086 %{
1087 "type" => "Image",
1088 "url" => [%{"href" => data["icon"]["url"]}]
1089 }
1090
1091 banner =
1092 data["image"]["url"] &&
1093 %{
1094 "type" => "Image",
1095 "url" => [%{"href" => data["image"]["url"]}]
1096 }
1097
1098 fields =
1099 data
1100 |> Map.get("attachment", [])
1101 |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
1102 |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
1103
1104 locked = data["manuallyApprovesFollowers"] || false
1105 data = Transmogrifier.maybe_fix_user_object(data)
1106 discoverable = data["discoverable"] || false
1107 invisible = data["invisible"] || false
1108
1109 user_data = %{
1110 ap_id: data["id"],
1111 ap_enabled: true,
1112 source_data: data,
1113 banner: banner,
1114 fields: fields,
1115 locked: locked,
1116 discoverable: discoverable,
1117 invisible: invisible,
1118 avatar: avatar,
1119 name: data["name"],
1120 follower_address: data["followers"],
1121 following_address: data["following"],
1122 bio: data["summary"]
1123 }
1124
1125 # nickname can be nil because of virtual actors
1126 user_data =
1127 if data["preferredUsername"] do
1128 Map.put(
1129 user_data,
1130 :nickname,
1131 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
1132 )
1133 else
1134 Map.put(user_data, :nickname, nil)
1135 end
1136
1137 {:ok, user_data}
1138 end
1139
1140 def fetch_follow_information_for_user(user) do
1141 with {:ok, following_data} <-
1142 Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
1143 following_count when is_integer(following_count) <- following_data["totalItems"],
1144 {:ok, hide_follows} <- collection_private(following_data),
1145 {:ok, followers_data} <-
1146 Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
1147 followers_count when is_integer(followers_count) <- followers_data["totalItems"],
1148 {:ok, hide_followers} <- collection_private(followers_data) do
1149 {:ok,
1150 %{
1151 hide_follows: hide_follows,
1152 follower_count: followers_count,
1153 following_count: following_count,
1154 hide_followers: hide_followers
1155 }}
1156 else
1157 {:error, _} = e ->
1158 e
1159
1160 e ->
1161 {:error, e}
1162 end
1163 end
1164
1165 defp maybe_update_follow_information(data) do
1166 with {:enabled, true} <-
1167 {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
1168 {:ok, info} <- fetch_follow_information_for_user(data) do
1169 info = Map.merge(data[:info] || %{}, info)
1170 Map.put(data, :info, info)
1171 else
1172 {:enabled, false} ->
1173 data
1174
1175 e ->
1176 Logger.error(
1177 "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
1178 )
1179
1180 data
1181 end
1182 end
1183
1184 defp collection_private(data) do
1185 if is_map(data["first"]) and
1186 data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
1187 {:ok, false}
1188 else
1189 with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
1190 Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
1191 {:ok, false}
1192 else
1193 {:error, {:ok, %{status: code}}} when code in [401, 403] ->
1194 {:ok, true}
1195
1196 {:error, _} = e ->
1197 e
1198
1199 e ->
1200 {:error, e}
1201 end
1202 end
1203 end
1204
1205 def user_data_from_user_object(data) do
1206 with {:ok, data} <- MRF.filter(data),
1207 {:ok, data} <- object_to_user_data(data) do
1208 {:ok, data}
1209 else
1210 e -> {:error, e}
1211 end
1212 end
1213
1214 def fetch_and_prepare_user_from_ap_id(ap_id) do
1215 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
1216 {:ok, data} <- user_data_from_user_object(data),
1217 data <- maybe_update_follow_information(data) do
1218 {:ok, data}
1219 else
1220 e ->
1221 Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
1222 {:error, e}
1223 end
1224 end
1225
1226 def make_user_from_ap_id(ap_id) do
1227 if _user = User.get_cached_by_ap_id(ap_id) do
1228 Transmogrifier.upgrade_user_from_ap_id(ap_id)
1229 else
1230 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
1231 User.insert_or_update_user(data)
1232 else
1233 e -> {:error, e}
1234 end
1235 end
1236 end
1237
1238 def make_user_from_nickname(nickname) do
1239 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
1240 make_user_from_ap_id(ap_id)
1241 else
1242 _e -> {:error, "No AP id in WebFinger"}
1243 end
1244 end
1245
1246 # filter out broken threads
1247 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
1248 entire_thread_visible_for_user?(activity, user)
1249 end
1250
1251 # do post-processing on a specific activity
1252 def contain_activity(%Activity{} = activity, %User{} = user) do
1253 contain_broken_threads(activity, user)
1254 end
1255
1256 def fetch_direct_messages_query do
1257 Activity
1258 |> restrict_type(%{"type" => "Create"})
1259 |> restrict_visibility(%{visibility: "direct"})
1260 |> order_by([activity], asc: activity.id)
1261 end
1262 end