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