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