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