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