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