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