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