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