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