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