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