eba8f80184018d61c83b3b538a0b2f1686378007
[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.Conversation
8 alias Pleroma.Notification
9 alias Pleroma.Object
10 alias Pleroma.Object.Fetcher
11 alias Pleroma.Pagination
12 alias Pleroma.Repo
13 alias Pleroma.Upload
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.MRF
16 alias Pleroma.Web.ActivityPub.Transmogrifier
17 alias Pleroma.Web.WebFinger
18
19 import Ecto.Query
20 import Pleroma.Web.ActivityPub.Utils
21 import Pleroma.Web.ActivityPub.Visibility
22
23 require Logger
24
25 # For Announce activities, we filter the recipients based on following status for any actors
26 # that match actual users. See issue #164 for more information about why this is necessary.
27 defp get_recipients(%{"type" => "Announce"} = data) do
28 to = Map.get(data, "to", [])
29 cc = Map.get(data, "cc", [])
30 bcc = Map.get(data, "bcc", [])
31 actor = User.get_cached_by_ap_id(data["actor"])
32
33 recipients =
34 Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
35 case User.get_cached_by_ap_id(recipient) do
36 nil -> true
37 user -> User.following?(user, actor)
38 end
39 end)
40
41 {recipients, to, cc}
42 end
43
44 defp get_recipients(%{"type" => "Create"} = data) do
45 to = Map.get(data, "to", [])
46 cc = Map.get(data, "cc", [])
47 bcc = Map.get(data, "bcc", [])
48 actor = Map.get(data, "actor", [])
49 recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
50 {recipients, to, cc}
51 end
52
53 defp get_recipients(data) do
54 to = Map.get(data, "to", [])
55 cc = Map.get(data, "cc", [])
56 bcc = Map.get(data, "bcc", [])
57 recipients = Enum.concat([to, cc, bcc])
58 {recipients, to, cc}
59 end
60
61 defp check_actor_is_active(actor) do
62 if not is_nil(actor) do
63 with user <- User.get_cached_by_ap_id(actor),
64 false <- user.info.deactivated do
65 :ok
66 else
67 _e -> :reject
68 end
69 else
70 :ok
71 end
72 end
73
74 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
75 limit = Pleroma.Config.get([:instance, :remote_limit])
76 String.length(content) <= limit
77 end
78
79 defp check_remote_limit(_), do: true
80
81 def increase_note_count_if_public(actor, object) do
82 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
83 end
84
85 def decrease_note_count_if_public(actor, object) do
86 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
87 end
88
89 def increase_replies_count_if_reply(%{
90 "object" => %{"inReplyTo" => reply_ap_id} = object,
91 "type" => "Create"
92 }) do
93 if is_public?(object) do
94 Object.increase_replies_count(reply_ap_id)
95 end
96 end
97
98 def increase_replies_count_if_reply(_create_data), do: :noop
99
100 def decrease_replies_count_if_reply(%Object{
101 data: %{"inReplyTo" => reply_ap_id} = object
102 }) do
103 if is_public?(object) do
104 Object.decrease_replies_count(reply_ap_id)
105 end
106 end
107
108 def decrease_replies_count_if_reply(_object), do: :noop
109
110 def insert(map, local \\ true, fake \\ false) when is_map(map) do
111 with nil <- Activity.normalize(map),
112 map <- lazy_put_activity_defaults(map, fake),
113 :ok <- check_actor_is_active(map["actor"]),
114 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
115 {:ok, map} <- MRF.filter(map),
116 {recipients, _, _} = get_recipients(map),
117 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
118 {:ok, map, object} <- insert_full_object(map) do
119 {:ok, activity} =
120 Repo.insert(%Activity{
121 data: map,
122 local: local,
123 actor: map["actor"],
124 recipients: recipients
125 })
126
127 # Splice in the child object if we have one.
128 activity =
129 if !is_nil(object) do
130 Map.put(activity, :object, object)
131 else
132 activity
133 end
134
135 Task.start(fn ->
136 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
137 end)
138
139 Notification.create_notifications(activity)
140
141 participations =
142 activity
143 |> Conversation.create_or_bump_for()
144 |> get_participations()
145
146 stream_out(activity)
147 stream_out_participations(participations)
148 {:ok, activity}
149 else
150 %Activity{} = activity ->
151 {:ok, activity}
152
153 {:fake, true, map, recipients} ->
154 activity = %Activity{
155 data: map,
156 local: local,
157 actor: map["actor"],
158 recipients: recipients,
159 id: "pleroma:fakeid"
160 }
161
162 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
163 {:ok, activity}
164
165 error ->
166 {:error, error}
167 end
168 end
169
170 defp get_participations({:ok, %{participations: participations}}), do: participations
171 defp get_participations(_), do: []
172
173 def stream_out_participations(participations) do
174 participations =
175 participations
176 |> Repo.preload(:user)
177
178 Enum.each(participations, fn participation ->
179 Pleroma.Web.Streamer.stream("participation", participation)
180 end)
181 end
182
183 def stream_out(activity) do
184 public = "https://www.w3.org/ns/activitystreams#Public"
185
186 if activity.data["type"] in ["Create", "Announce", "Delete"] do
187 Pleroma.Web.Streamer.stream("user", activity)
188 Pleroma.Web.Streamer.stream("list", activity)
189
190 if Enum.member?(activity.data["to"], public) do
191 Pleroma.Web.Streamer.stream("public", activity)
192
193 if activity.local do
194 Pleroma.Web.Streamer.stream("public:local", activity)
195 end
196
197 if activity.data["type"] in ["Create"] do
198 object = Object.normalize(activity)
199
200 object.data
201 |> Map.get("tag", [])
202 |> Enum.filter(fn tag -> is_bitstring(tag) end)
203 |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
204
205 if object.data["attachment"] != [] do
206 Pleroma.Web.Streamer.stream("public:media", activity)
207
208 if activity.local do
209 Pleroma.Web.Streamer.stream("public:local:media", activity)
210 end
211 end
212 end
213 else
214 # TODO: Write test, replace with visibility test
215 if !Enum.member?(activity.data["cc"] || [], public) &&
216 !Enum.member?(
217 activity.data["to"],
218 User.get_cached_by_ap_id(activity.data["actor"]).follower_address
219 ),
220 do: Pleroma.Web.Streamer.stream("direct", activity)
221 end
222 end
223 end
224
225 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
226 additional = params[:additional] || %{}
227 # only accept false as false value
228 local = !(params[:local] == false)
229 published = params[:published]
230
231 with create_data <-
232 make_create_data(
233 %{to: to, actor: actor, published: published, context: context, object: object},
234 additional
235 ),
236 {:ok, activity} <- insert(create_data, local, fake),
237 {:fake, false, activity} <- {:fake, fake, activity},
238 _ <- increase_replies_count_if_reply(create_data),
239 # Changing note count prior to enqueuing federation task in order to avoid
240 # race conditions on updating user.info
241 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
242 :ok <- maybe_federate(activity) do
243 {:ok, activity}
244 else
245 {:fake, true, activity} ->
246 {:ok, activity}
247 end
248 end
249
250 def accept(%{to: to, actor: actor, object: object} = params) do
251 # only accept false as false value
252 local = !(params[:local] == false)
253
254 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
255 {:ok, activity} <- insert(data, local),
256 :ok <- maybe_federate(activity) do
257 {:ok, activity}
258 end
259 end
260
261 def reject(%{to: to, actor: actor, object: object} = params) do
262 # only accept false as false value
263 local = !(params[:local] == false)
264
265 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
266 {:ok, activity} <- insert(data, local),
267 :ok <- maybe_federate(activity) do
268 {:ok, activity}
269 end
270 end
271
272 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
273 # only accept false as false value
274 local = !(params[:local] == false)
275
276 with data <- %{
277 "to" => to,
278 "cc" => cc,
279 "type" => "Update",
280 "actor" => actor,
281 "object" => object
282 },
283 {:ok, activity} <- insert(data, local),
284 :ok <- maybe_federate(activity) do
285 {:ok, activity}
286 end
287 end
288
289 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
290 def like(
291 %User{ap_id: ap_id} = user,
292 %Object{data: %{"id" => _}} = object,
293 activity_id \\ nil,
294 local \\ true
295 ) do
296 with nil <- get_existing_like(ap_id, object),
297 like_data <- make_like_data(user, object, activity_id),
298 {:ok, activity} <- insert(like_data, local),
299 {:ok, object} <- add_like_to_object(activity, object),
300 :ok <- maybe_federate(activity) do
301 {:ok, activity, object}
302 else
303 %Activity{} = activity -> {:ok, activity, object}
304 error -> {:error, error}
305 end
306 end
307
308 def unlike(
309 %User{} = actor,
310 %Object{} = object,
311 activity_id \\ nil,
312 local \\ true
313 ) do
314 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
315 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
316 {:ok, unlike_activity} <- insert(unlike_data, local),
317 {:ok, _activity} <- Repo.delete(like_activity),
318 {:ok, object} <- remove_like_from_object(like_activity, object),
319 :ok <- maybe_federate(unlike_activity) do
320 {:ok, unlike_activity, like_activity, object}
321 else
322 _e -> {:ok, object}
323 end
324 end
325
326 def announce(
327 %User{ap_id: _} = user,
328 %Object{data: %{"id" => _}} = object,
329 activity_id \\ nil,
330 local \\ true,
331 public \\ true
332 ) do
333 with true <- is_public?(object),
334 announce_data <- make_announce_data(user, object, activity_id, public),
335 {:ok, activity} <- insert(announce_data, local),
336 {:ok, object} <- add_announce_to_object(activity, object),
337 :ok <- maybe_federate(activity) do
338 {:ok, activity, object}
339 else
340 error -> {:error, error}
341 end
342 end
343
344 def unannounce(
345 %User{} = actor,
346 %Object{} = object,
347 activity_id \\ nil,
348 local \\ true
349 ) do
350 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
351 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
352 {:ok, unannounce_activity} <- insert(unannounce_data, local),
353 :ok <- maybe_federate(unannounce_activity),
354 {:ok, _activity} <- Repo.delete(announce_activity),
355 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
356 {:ok, unannounce_activity, object}
357 else
358 _e -> {:ok, object}
359 end
360 end
361
362 def follow(follower, followed, activity_id \\ nil, local \\ true) do
363 with data <- make_follow_data(follower, followed, activity_id),
364 {:ok, activity} <- insert(data, local),
365 :ok <- maybe_federate(activity) do
366 {:ok, activity}
367 end
368 end
369
370 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
371 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
372 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
373 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
374 {:ok, activity} <- insert(unfollow_data, local),
375 :ok <- maybe_federate(activity) do
376 {:ok, activity}
377 end
378 end
379
380 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
381 user = User.get_cached_by_ap_id(actor)
382 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
383
384 with {:ok, object, activity} <- Object.delete(object),
385 data <- %{
386 "type" => "Delete",
387 "actor" => actor,
388 "object" => id,
389 "to" => to,
390 "deleted_activity_id" => activity && activity.id
391 },
392 {:ok, activity} <- insert(data, local),
393 _ <- decrease_replies_count_if_reply(object),
394 # Changing note count prior to enqueuing federation task in order to avoid
395 # race conditions on updating user.info
396 {:ok, _actor} <- decrease_note_count_if_public(user, object),
397 :ok <- maybe_federate(activity) do
398 {:ok, activity}
399 end
400 end
401
402 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
403 ap_config = Application.get_env(:pleroma, :activitypub)
404 unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked)
405 outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
406
407 with true <- unfollow_blocked do
408 follow_activity = fetch_latest_follow(blocker, blocked)
409
410 if follow_activity do
411 unfollow(blocker, blocked, nil, local)
412 end
413 end
414
415 with true <- outgoing_blocks,
416 block_data <- make_block_data(blocker, blocked, activity_id),
417 {:ok, activity} <- insert(block_data, local),
418 :ok <- maybe_federate(activity) do
419 {:ok, activity}
420 else
421 _e -> {:ok, nil}
422 end
423 end
424
425 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
426 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
427 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
428 {:ok, activity} <- insert(unblock_data, local),
429 :ok <- maybe_federate(activity) do
430 {:ok, activity}
431 end
432 end
433
434 def flag(
435 %{
436 actor: actor,
437 context: context,
438 account: account,
439 statuses: statuses,
440 content: content
441 } = params
442 ) do
443 # only accept false as false value
444 local = !(params[:local] == false)
445 forward = !(params[:forward] == false)
446
447 additional = params[:additional] || %{}
448
449 params = %{
450 actor: actor,
451 context: context,
452 account: account,
453 statuses: statuses,
454 content: content
455 }
456
457 additional =
458 if forward do
459 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
460 else
461 Map.merge(additional, %{"to" => [], "cc" => []})
462 end
463
464 with flag_data <- make_flag_data(params, additional),
465 {:ok, activity} <- insert(flag_data, local),
466 :ok <- maybe_federate(activity) do
467 Enum.each(User.all_superusers(), fn superuser ->
468 superuser
469 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
470 |> Pleroma.Emails.Mailer.deliver_async()
471 end)
472
473 {:ok, activity}
474 end
475 end
476
477 defp fetch_activities_for_context_query(context, opts) do
478 public = ["https://www.w3.org/ns/activitystreams#Public"]
479
480 recipients =
481 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
482
483 from(activity in Activity)
484 |> restrict_blocked(opts)
485 |> restrict_recipients(recipients, opts["user"])
486 |> where(
487 [activity],
488 fragment(
489 "?->>'type' = ? and ?->>'context' = ?",
490 activity.data,
491 "Create",
492 activity.data,
493 ^context
494 )
495 )
496 |> order_by([activity], desc: activity.id)
497 end
498
499 @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
500 def fetch_activities_for_context(context, opts \\ %{}) do
501 context
502 |> fetch_activities_for_context_query(opts)
503 |> Activity.with_preloaded_object()
504 |> Repo.all()
505 end
506
507 @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
508 Pleroma.FlakeId.t() | nil
509 def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
510 context
511 |> fetch_activities_for_context_query(opts)
512 |> limit(1)
513 |> select([a], a.id)
514 |> Repo.one()
515 end
516
517 def fetch_public_activities(opts \\ %{}) do
518 q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
519
520 q
521 |> restrict_unlisted()
522 |> Pagination.fetch_paginated(opts)
523 |> Enum.reverse()
524 end
525
526 @valid_visibilities ~w[direct unlisted public private]
527
528 defp restrict_visibility(query, %{visibility: visibility})
529 when is_list(visibility) do
530 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
531 query =
532 from(
533 a in query,
534 where:
535 fragment(
536 "activity_visibility(?, ?, ?) = ANY (?)",
537 a.actor,
538 a.recipients,
539 a.data,
540 ^visibility
541 )
542 )
543
544 Ecto.Adapters.SQL.to_sql(:all, Repo, query)
545
546 query
547 else
548 Logger.error("Could not restrict visibility to #{visibility}")
549 end
550 end
551
552 defp restrict_visibility(query, %{visibility: visibility})
553 when visibility in @valid_visibilities do
554 query =
555 from(
556 a in query,
557 where:
558 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
559 )
560
561 Ecto.Adapters.SQL.to_sql(:all, Repo, query)
562
563 query
564 end
565
566 defp restrict_visibility(_query, %{visibility: visibility})
567 when visibility not in @valid_visibilities do
568 Logger.error("Could not restrict visibility to #{visibility}")
569 end
570
571 defp restrict_visibility(query, _visibility), do: query
572
573 def fetch_user_activities(user, reading_user, params \\ %{}) do
574 params =
575 params
576 |> Map.put("type", ["Create", "Announce"])
577 |> Map.put("actor_id", user.ap_id)
578 |> Map.put("whole_db", true)
579 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
580
581 recipients =
582 if reading_user do
583 ["https://www.w3.org/ns/activitystreams#Public"] ++
584 [reading_user.ap_id | reading_user.following]
585 else
586 ["https://www.w3.org/ns/activitystreams#Public"]
587 end
588
589 fetch_activities(recipients, params)
590 |> Enum.reverse()
591 end
592
593 defp restrict_since(query, %{"since_id" => ""}), do: query
594
595 defp restrict_since(query, %{"since_id" => since_id}) do
596 from(activity in query, where: activity.id > ^since_id)
597 end
598
599 defp restrict_since(query, _), do: query
600
601 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
602 raise "Can't use the child object without preloading!"
603 end
604
605 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
606 when is_list(tag_reject) and tag_reject != [] do
607 from(
608 [_activity, object] in query,
609 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
610 )
611 end
612
613 defp restrict_tag_reject(query, _), do: query
614
615 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
616 raise "Can't use the child object without preloading!"
617 end
618
619 defp restrict_tag_all(query, %{"tag_all" => tag_all})
620 when is_list(tag_all) and tag_all != [] do
621 from(
622 [_activity, object] in query,
623 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
624 )
625 end
626
627 defp restrict_tag_all(query, _), do: query
628
629 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
630 raise "Can't use the child object without preloading!"
631 end
632
633 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
634 from(
635 [_activity, object] in query,
636 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
637 )
638 end
639
640 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
641 from(
642 [_activity, object] in query,
643 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
644 )
645 end
646
647 defp restrict_tag(query, _), do: query
648
649 defp restrict_to_cc(query, recipients_to, recipients_cc) do
650 from(
651 activity in query,
652 where:
653 fragment(
654 "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
655 activity.data,
656 ^recipients_to,
657 activity.data,
658 ^recipients_cc
659 )
660 )
661 end
662
663 defp restrict_recipients(query, [], _user), do: query
664
665 defp restrict_recipients(query, recipients, nil) do
666 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
667 end
668
669 defp restrict_recipients(query, recipients, user) do
670 from(
671 activity in query,
672 where: fragment("? && ?", ^recipients, activity.recipients),
673 or_where: activity.actor == ^user.ap_id
674 )
675 end
676
677 defp restrict_local(query, %{"local_only" => true}) do
678 from(activity in query, where: activity.local == true)
679 end
680
681 defp restrict_local(query, _), do: query
682
683 defp restrict_actor(query, %{"actor_id" => actor_id}) do
684 from(activity in query, where: activity.actor == ^actor_id)
685 end
686
687 defp restrict_actor(query, _), do: query
688
689 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
690 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
691 end
692
693 defp restrict_type(query, %{"type" => type}) do
694 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
695 end
696
697 defp restrict_type(query, _), do: query
698
699 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
700 from(
701 activity in query,
702 where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
703 )
704 end
705
706 defp restrict_favorited_by(query, _), do: query
707
708 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
709 raise "Can't use the child object without preloading!"
710 end
711
712 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
713 from(
714 [_activity, object] in query,
715 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
716 )
717 end
718
719 defp restrict_media(query, _), do: query
720
721 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
722 from(
723 activity in query,
724 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
725 )
726 end
727
728 defp restrict_replies(query, _), do: query
729
730 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
731 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
732 end
733
734 defp restrict_reblogs(query, _), do: query
735
736 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
737
738 defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
739 mutes = info.mutes
740
741 from(
742 activity in query,
743 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
744 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
745 )
746 end
747
748 defp restrict_muted(query, _), do: query
749
750 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
751 blocks = info.blocks || []
752 domain_blocks = info.domain_blocks || []
753
754 from(
755 activity in query,
756 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
757 where: fragment("not (? && ?)", activity.recipients, ^blocks),
758 where:
759 fragment(
760 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
761 activity.data,
762 activity.data,
763 ^blocks
764 ),
765 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
766 )
767 end
768
769 defp restrict_blocked(query, _), do: query
770
771 defp restrict_unlisted(query) do
772 from(
773 activity in query,
774 where:
775 fragment(
776 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
777 activity.data,
778 ^["https://www.w3.org/ns/activitystreams#Public"]
779 )
780 )
781 end
782
783 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
784 from(activity in query, where: activity.id in ^ids)
785 end
786
787 defp restrict_pinned(query, _), do: query
788
789 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
790 muted_reblogs = info.muted_reblogs || []
791
792 from(
793 activity in query,
794 where:
795 fragment(
796 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
797 activity.data,
798 activity.actor,
799 ^muted_reblogs
800 )
801 )
802 end
803
804 defp restrict_muted_reblogs(query, _), do: query
805
806 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
807
808 defp maybe_preload_objects(query, _) do
809 query
810 |> Activity.with_preloaded_object()
811 end
812
813 defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
814
815 defp maybe_preload_bookmarks(query, opts) do
816 query
817 |> Activity.with_preloaded_bookmark(opts["user"])
818 end
819
820 defp maybe_order(query, %{order: :desc}) do
821 query
822 |> order_by(desc: :id)
823 end
824
825 defp maybe_order(query, %{order: :asc}) do
826 query
827 |> order_by(asc: :id)
828 end
829
830 defp maybe_order(query, _), do: query
831
832 def fetch_activities_query(recipients, opts \\ %{}) do
833 Activity
834 |> maybe_preload_objects(opts)
835 |> maybe_preload_bookmarks(opts)
836 |> maybe_order(opts)
837 |> restrict_recipients(recipients, opts["user"])
838 |> restrict_tag(opts)
839 |> restrict_tag_reject(opts)
840 |> restrict_tag_all(opts)
841 |> restrict_since(opts)
842 |> restrict_local(opts)
843 |> restrict_actor(opts)
844 |> restrict_type(opts)
845 |> restrict_favorited_by(opts)
846 |> restrict_blocked(opts)
847 |> restrict_muted(opts)
848 |> restrict_media(opts)
849 |> restrict_visibility(opts)
850 |> restrict_replies(opts)
851 |> restrict_reblogs(opts)
852 |> restrict_pinned(opts)
853 |> restrict_muted_reblogs(opts)
854 end
855
856 def fetch_activities(recipients, opts \\ %{}) do
857 list_memberships = Pleroma.List.memberships(opts["user"])
858
859 fetch_activities_query(recipients ++ list_memberships, opts)
860 |> Pagination.fetch_paginated(opts)
861 |> Enum.reverse()
862 |> maybe_update_cc(list_memberships, opts["user"])
863 end
864
865 defp maybe_update_cc(activities, [], _), do: activities
866 defp maybe_update_cc(activities, _, nil), do: activities
867
868 defp maybe_update_cc(activities, list_memberships, user) do
869 Enum.map(activities, fn
870 %{data: %{"bcc" => bcc}} = activity when is_list(bcc) ->
871 if Enum.any?(bcc, &(&1 in list_memberships)) do
872 update_in(activity.data["cc"], &[user.ap_id | &1])
873 else
874 activity
875 end
876
877 activity ->
878 activity
879 end)
880 end
881
882 def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
883 fetch_activities_query([], opts)
884 |> restrict_to_cc(recipients_to, recipients_cc)
885 |> Pagination.fetch_paginated(opts)
886 |> Enum.reverse()
887 end
888
889 def upload(file, opts \\ []) do
890 with {:ok, data} <- Upload.store(file, opts) do
891 obj_data =
892 if opts[:actor] do
893 Map.put(data, "actor", opts[:actor])
894 else
895 data
896 end
897
898 Repo.insert(%Object{data: obj_data})
899 end
900 end
901
902 def user_data_from_user_object(data) do
903 avatar =
904 data["icon"]["url"] &&
905 %{
906 "type" => "Image",
907 "url" => [%{"href" => data["icon"]["url"]}]
908 }
909
910 banner =
911 data["image"]["url"] &&
912 %{
913 "type" => "Image",
914 "url" => [%{"href" => data["image"]["url"]}]
915 }
916
917 locked = data["manuallyApprovesFollowers"] || false
918 data = Transmogrifier.maybe_fix_user_object(data)
919
920 user_data = %{
921 ap_id: data["id"],
922 info: %{
923 "ap_enabled" => true,
924 "source_data" => data,
925 "banner" => banner,
926 "locked" => locked
927 },
928 avatar: avatar,
929 name: data["name"],
930 follower_address: data["followers"],
931 bio: data["summary"]
932 }
933
934 # nickname can be nil because of virtual actors
935 user_data =
936 if data["preferredUsername"] do
937 Map.put(
938 user_data,
939 :nickname,
940 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
941 )
942 else
943 Map.put(user_data, :nickname, nil)
944 end
945
946 {:ok, user_data}
947 end
948
949 def fetch_and_prepare_user_from_ap_id(ap_id) do
950 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
951 user_data_from_user_object(data)
952 else
953 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
954 end
955 end
956
957 def make_user_from_ap_id(ap_id) do
958 if _user = User.get_cached_by_ap_id(ap_id) do
959 Transmogrifier.upgrade_user_from_ap_id(ap_id)
960 else
961 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
962 User.insert_or_update_user(data)
963 else
964 e -> {:error, e}
965 end
966 end
967 end
968
969 def make_user_from_nickname(nickname) do
970 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
971 make_user_from_ap_id(ap_id)
972 else
973 _e -> {:error, "No AP id in WebFinger"}
974 end
975 end
976
977 # filter out broken threads
978 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
979 entire_thread_visible_for_user?(activity, user)
980 end
981
982 # do post-processing on a specific activity
983 def contain_activity(%Activity{} = activity, %User{} = user) do
984 contain_broken_threads(activity, user)
985 end
986
987 # do post-processing on a timeline
988 def contain_timeline(timeline, user) do
989 timeline
990 |> Enum.filter(fn activity ->
991 contain_activity(activity, user)
992 end)
993 end
994 end