Fix media timeline depending on embeded object and add some guards
[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.Instances
8 alias Pleroma.Notification
9 alias Pleroma.Object
10 alias Pleroma.Object.Fetcher
11 alias Pleroma.Pagination
12 alias Pleroma.Repo
13 alias Pleroma.Upload
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.MRF
16 alias Pleroma.Web.ActivityPub.Transmogrifier
17 alias Pleroma.Web.Federator
18 alias Pleroma.Web.WebFinger
19
20 import Ecto.Query
21 import Pleroma.Web.ActivityPub.Utils
22 import Pleroma.Web.ActivityPub.Visibility
23
24 require Logger
25
26 @httpoison Application.get_env(:pleroma, :httpoison)
27
28 # For Announce activities, we filter the recipients based on following status for any actors
29 # that match actual users. See issue #164 for more information about why this is necessary.
30 defp get_recipients(%{"type" => "Announce"} = data) do
31 to = data["to"] || []
32 cc = data["cc"] || []
33 actor = User.get_cached_by_ap_id(data["actor"])
34
35 recipients =
36 (to ++ cc)
37 |> Enum.filter(fn recipient ->
38 case User.get_cached_by_ap_id(recipient) do
39 nil ->
40 true
41
42 user ->
43 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 = data["to"] || []
52 cc = data["cc"] || []
53 actor = data["actor"] || []
54 recipients = (to ++ cc ++ [actor]) |> Enum.uniq()
55 {recipients, to, cc}
56 end
57
58 defp get_recipients(data) do
59 to = data["to"] || []
60 cc = data["cc"] || []
61 recipients = to ++ cc
62 {recipients, to, cc}
63 end
64
65 defp check_actor_is_active(actor) do
66 if not is_nil(actor) do
67 with user <- User.get_cached_by_ap_id(actor),
68 false <- user.info.deactivated do
69 :ok
70 else
71 _e -> :reject
72 end
73 else
74 :ok
75 end
76 end
77
78 defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
79 limit = Pleroma.Config.get([:instance, :remote_limit])
80 String.length(content) <= limit
81 end
82
83 defp check_remote_limit(_), do: true
84
85 def increase_note_count_if_public(actor, object) do
86 if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
87 end
88
89 def decrease_note_count_if_public(actor, object) do
90 if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
91 end
92
93 def increase_replies_count_if_reply(%{
94 "object" => %{"inReplyTo" => reply_ap_id} = object,
95 "type" => "Create"
96 }) do
97 if is_public?(object) do
98 Object.increase_replies_count(reply_ap_id)
99 end
100 end
101
102 def increase_replies_count_if_reply(_create_data), do: :noop
103
104 def decrease_replies_count_if_reply(%Object{
105 data: %{"inReplyTo" => reply_ap_id} = object
106 }) do
107 if is_public?(object) do
108 Object.decrease_replies_count(reply_ap_id)
109 end
110 end
111
112 def decrease_replies_count_if_reply(_object), do: :noop
113
114 def insert(map, local \\ true, fake \\ false) when is_map(map) do
115 with nil <- Activity.normalize(map),
116 map <- lazy_put_activity_defaults(map, fake),
117 :ok <- check_actor_is_active(map["actor"]),
118 {_, true} <- {:remote_limit_error, check_remote_limit(map)},
119 {:ok, map} <- MRF.filter(map),
120 {recipients, _, _} = get_recipients(map),
121 {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
122 {:ok, map, object} <- insert_full_object(map) do
123 {:ok, activity} =
124 Repo.insert(%Activity{
125 data: map,
126 local: local,
127 actor: map["actor"],
128 recipients: recipients
129 })
130
131 # Splice in the child object if we have one.
132 activity =
133 if !is_nil(object) do
134 Map.put(activity, :object, object)
135 else
136 activity
137 end
138
139 Task.start(fn ->
140 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
141 end)
142
143 Notification.create_notifications(activity)
144 stream_out(activity)
145 {:ok, activity}
146 else
147 %Activity{} = activity ->
148 {:ok, activity}
149
150 {:fake, true, map, recipients} ->
151 activity = %Activity{
152 data: map,
153 local: local,
154 actor: map["actor"],
155 recipients: recipients,
156 id: "pleroma:fakeid"
157 }
158
159 Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
160 {:ok, activity}
161
162 error ->
163 {:error, error}
164 end
165 end
166
167 def stream_out(activity) do
168 public = "https://www.w3.org/ns/activitystreams#Public"
169
170 if activity.data["type"] in ["Create", "Announce"] do
171 object = Object.normalize(activity.data["object"])
172 Pleroma.Web.Streamer.stream("user", activity)
173 Pleroma.Web.Streamer.stream("list", activity)
174
175 if Enum.member?(activity.data["to"], public) do
176 Pleroma.Web.Streamer.stream("public", activity)
177
178 if activity.local do
179 Pleroma.Web.Streamer.stream("public:local", activity)
180 end
181
182 if activity.data["type"] in ["Create"] do
183 object.data
184 |> Map.get("tag", [])
185 |> Enum.filter(fn tag -> is_bitstring(tag) end)
186 |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
187
188 if object.data["attachment"] != [] do
189 Pleroma.Web.Streamer.stream("public:media", activity)
190
191 if activity.local do
192 Pleroma.Web.Streamer.stream("public:local:media", activity)
193 end
194 end
195 end
196 else
197 if !Enum.member?(activity.data["cc"] || [], public) &&
198 !Enum.member?(
199 activity.data["to"],
200 User.get_by_ap_id(activity.data["actor"]).follower_address
201 ),
202 do: Pleroma.Web.Streamer.stream("direct", activity)
203 end
204 end
205 end
206
207 def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
208 additional = params[:additional] || %{}
209 # only accept false as false value
210 local = !(params[:local] == false)
211 published = params[:published]
212
213 with create_data <-
214 make_create_data(
215 %{to: to, actor: actor, published: published, context: context, object: object},
216 additional
217 ),
218 {:ok, activity} <- insert(create_data, local, fake),
219 {:fake, false, activity} <- {:fake, fake, activity},
220 _ <- increase_replies_count_if_reply(create_data),
221 # Changing note count prior to enqueuing federation task in order to avoid
222 # race conditions on updating user.info
223 {:ok, _actor} <- increase_note_count_if_public(actor, activity),
224 :ok <- maybe_federate(activity) do
225 {:ok, activity}
226 else
227 {:fake, true, activity} ->
228 {:ok, activity}
229 end
230 end
231
232 def accept(%{to: to, actor: actor, object: object} = params) do
233 # only accept false as false value
234 local = !(params[:local] == false)
235
236 with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
237 {:ok, activity} <- insert(data, local),
238 :ok <- maybe_federate(activity) do
239 {:ok, activity}
240 end
241 end
242
243 def reject(%{to: to, actor: actor, object: object} = params) do
244 # only accept false as false value
245 local = !(params[:local] == false)
246
247 with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
248 {:ok, activity} <- insert(data, local),
249 :ok <- maybe_federate(activity) do
250 {:ok, activity}
251 end
252 end
253
254 def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
255 # only accept false as false value
256 local = !(params[:local] == false)
257
258 with data <- %{
259 "to" => to,
260 "cc" => cc,
261 "type" => "Update",
262 "actor" => actor,
263 "object" => object
264 },
265 {:ok, activity} <- insert(data, local),
266 :ok <- maybe_federate(activity) do
267 {:ok, activity}
268 end
269 end
270
271 # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
272 def like(
273 %User{ap_id: ap_id} = user,
274 %Object{data: %{"id" => _}} = object,
275 activity_id \\ nil,
276 local \\ true
277 ) do
278 with nil <- get_existing_like(ap_id, object),
279 like_data <- make_like_data(user, object, activity_id),
280 {:ok, activity} <- insert(like_data, local),
281 {:ok, object} <- add_like_to_object(activity, object),
282 :ok <- maybe_federate(activity) do
283 {:ok, activity, object}
284 else
285 %Activity{} = activity -> {:ok, activity, object}
286 error -> {:error, error}
287 end
288 end
289
290 def unlike(
291 %User{} = actor,
292 %Object{} = object,
293 activity_id \\ nil,
294 local \\ true
295 ) do
296 with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
297 unlike_data <- make_unlike_data(actor, like_activity, activity_id),
298 {:ok, unlike_activity} <- insert(unlike_data, local),
299 {:ok, _activity} <- Repo.delete(like_activity),
300 {:ok, object} <- remove_like_from_object(like_activity, object),
301 :ok <- maybe_federate(unlike_activity) do
302 {:ok, unlike_activity, like_activity, object}
303 else
304 _e -> {:ok, object}
305 end
306 end
307
308 def announce(
309 %User{ap_id: _} = user,
310 %Object{data: %{"id" => _}} = object,
311 activity_id \\ nil,
312 local \\ true,
313 public \\ true
314 ) do
315 with true <- is_public?(object),
316 announce_data <- make_announce_data(user, object, activity_id, public),
317 {:ok, activity} <- insert(announce_data, local),
318 {:ok, object} <- add_announce_to_object(activity, object),
319 :ok <- maybe_federate(activity) do
320 {:ok, activity, object}
321 else
322 error -> {:error, error}
323 end
324 end
325
326 def unannounce(
327 %User{} = actor,
328 %Object{} = object,
329 activity_id \\ nil,
330 local \\ true
331 ) do
332 with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
333 unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
334 {:ok, unannounce_activity} <- insert(unannounce_data, local),
335 :ok <- maybe_federate(unannounce_activity),
336 {:ok, _activity} <- Repo.delete(announce_activity),
337 {:ok, object} <- remove_announce_from_object(announce_activity, object) do
338 {:ok, unannounce_activity, object}
339 else
340 _e -> {:ok, object}
341 end
342 end
343
344 def follow(follower, followed, activity_id \\ nil, local \\ true) do
345 with data <- make_follow_data(follower, followed, activity_id),
346 {:ok, activity} <- insert(data, local),
347 :ok <- maybe_federate(activity) do
348 {:ok, activity}
349 end
350 end
351
352 def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
353 with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
354 {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
355 unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
356 {:ok, activity} <- insert(unfollow_data, local),
357 :ok <- maybe_federate(activity) do
358 {:ok, activity}
359 end
360 end
361
362 def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
363 user = User.get_cached_by_ap_id(actor)
364 to = (object.data["to"] || []) ++ (object.data["cc"] || [])
365
366 with {:ok, object, activity} <- Object.delete(object),
367 data <- %{
368 "type" => "Delete",
369 "actor" => actor,
370 "object" => id,
371 "to" => to,
372 "deleted_activity_id" => activity && activity.id
373 },
374 {:ok, activity} <- insert(data, local),
375 _ <- decrease_replies_count_if_reply(object),
376 # Changing note count prior to enqueuing federation task in order to avoid
377 # race conditions on updating user.info
378 {:ok, _actor} <- decrease_note_count_if_public(user, object),
379 :ok <- maybe_federate(activity) do
380 {:ok, activity}
381 end
382 end
383
384 def block(blocker, blocked, activity_id \\ nil, local \\ true) do
385 ap_config = Application.get_env(:pleroma, :activitypub)
386 unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked)
387 outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
388
389 with true <- unfollow_blocked do
390 follow_activity = fetch_latest_follow(blocker, blocked)
391
392 if follow_activity do
393 unfollow(blocker, blocked, nil, local)
394 end
395 end
396
397 with true <- outgoing_blocks,
398 block_data <- make_block_data(blocker, blocked, activity_id),
399 {:ok, activity} <- insert(block_data, local),
400 :ok <- maybe_federate(activity) do
401 {:ok, activity}
402 else
403 _e -> {:ok, nil}
404 end
405 end
406
407 def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
408 with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
409 unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
410 {:ok, activity} <- insert(unblock_data, local),
411 :ok <- maybe_federate(activity) do
412 {:ok, activity}
413 end
414 end
415
416 def flag(
417 %{
418 actor: actor,
419 context: context,
420 account: account,
421 statuses: statuses,
422 content: content
423 } = params
424 ) do
425 # only accept false as false value
426 local = !(params[:local] == false)
427 forward = !(params[:forward] == false)
428
429 additional = params[:additional] || %{}
430
431 params = %{
432 actor: actor,
433 context: context,
434 account: account,
435 statuses: statuses,
436 content: content
437 }
438
439 additional =
440 if forward do
441 Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
442 else
443 Map.merge(additional, %{"to" => [], "cc" => []})
444 end
445
446 with flag_data <- make_flag_data(params, additional),
447 {:ok, activity} <- insert(flag_data, local),
448 :ok <- maybe_federate(activity) do
449 Enum.each(User.all_superusers(), fn superuser ->
450 superuser
451 |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
452 |> Pleroma.Emails.Mailer.deliver_async()
453 end)
454
455 {:ok, activity}
456 end
457 end
458
459 def fetch_activities_for_context(context, opts \\ %{}) do
460 public = ["https://www.w3.org/ns/activitystreams#Public"]
461
462 recipients =
463 if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
464
465 query = from(activity in Activity)
466
467 query =
468 query
469 |> restrict_blocked(opts)
470 |> restrict_recipients(recipients, opts["user"])
471
472 query =
473 from(
474 activity in query,
475 where:
476 fragment(
477 "?->>'type' = ? and ?->>'context' = ?",
478 activity.data,
479 "Create",
480 activity.data,
481 ^context
482 ),
483 order_by: [desc: :id]
484 )
485 |> Activity.with_preloaded_object()
486
487 Repo.all(query)
488 end
489
490 def fetch_public_activities(opts \\ %{}) do
491 q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
492
493 q
494 |> restrict_unlisted()
495 |> Pagination.fetch_paginated(opts)
496 |> Enum.reverse()
497 end
498
499 @valid_visibilities ~w[direct unlisted public private]
500
501 defp restrict_visibility(query, %{visibility: visibility})
502 when is_list(visibility) do
503 if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
504 query =
505 from(
506 a in query,
507 where:
508 fragment(
509 "activity_visibility(?, ?, ?) = ANY (?)",
510 a.actor,
511 a.recipients,
512 a.data,
513 ^visibility
514 )
515 )
516
517 Ecto.Adapters.SQL.to_sql(:all, Repo, query)
518
519 query
520 else
521 Logger.error("Could not restrict visibility to #{visibility}")
522 end
523 end
524
525 defp restrict_visibility(query, %{visibility: visibility})
526 when visibility in @valid_visibilities do
527 query =
528 from(
529 a in query,
530 where:
531 fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
532 )
533
534 Ecto.Adapters.SQL.to_sql(:all, Repo, query)
535
536 query
537 end
538
539 defp restrict_visibility(_query, %{visibility: visibility})
540 when visibility not in @valid_visibilities do
541 Logger.error("Could not restrict visibility to #{visibility}")
542 end
543
544 defp restrict_visibility(query, _visibility), do: query
545
546 def fetch_user_activities(user, reading_user, params \\ %{}) do
547 params =
548 params
549 |> Map.put("type", ["Create", "Announce"])
550 |> Map.put("actor_id", user.ap_id)
551 |> Map.put("whole_db", true)
552 |> Map.put("pinned_activity_ids", user.info.pinned_activities)
553
554 recipients =
555 if reading_user do
556 ["https://www.w3.org/ns/activitystreams#Public"] ++
557 [reading_user.ap_id | reading_user.following]
558 else
559 ["https://www.w3.org/ns/activitystreams#Public"]
560 end
561
562 fetch_activities(recipients, params)
563 |> Enum.reverse()
564 end
565
566 defp restrict_since(query, %{"since_id" => ""}), do: query
567
568 defp restrict_since(query, %{"since_id" => since_id}) do
569 from(activity in query, where: activity.id > ^since_id)
570 end
571
572 defp restrict_since(query, _), do: query
573
574 defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
575 raise "Can't use the child object without preloading!"
576 end
577
578 defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
579 when is_list(tag_reject) and tag_reject != [] do
580 from(
581 [_activity, object] in query,
582 where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
583 )
584 end
585
586 defp restrict_tag_reject(query, _), do: query
587
588 defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
589 raise "Can't use the child object without preloading!"
590 end
591
592 defp restrict_tag_all(query, %{"tag_all" => tag_all})
593 when is_list(tag_all) and tag_all != [] do
594 from(
595 [_activity, object] in query,
596 where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
597 )
598 end
599
600 defp restrict_tag_all(query, _), do: query
601
602 defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
603 raise "Can't use the child object without preloading!"
604 end
605
606 defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
607 from(
608 [_activity, object] in query,
609 where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
610 )
611 end
612
613 defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
614 from(
615 [_activity, object] in query,
616 where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
617 )
618 end
619
620 defp restrict_tag(query, _), do: query
621
622 defp restrict_to_cc(query, recipients_to, recipients_cc) do
623 from(
624 activity in query,
625 where:
626 fragment(
627 "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
628 activity.data,
629 ^recipients_to,
630 activity.data,
631 ^recipients_cc
632 )
633 )
634 end
635
636 defp restrict_recipients(query, [], _user), do: query
637
638 defp restrict_recipients(query, recipients, nil) do
639 from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
640 end
641
642 defp restrict_recipients(query, recipients, user) do
643 from(
644 activity in query,
645 where: fragment("? && ?", ^recipients, activity.recipients),
646 or_where: activity.actor == ^user.ap_id
647 )
648 end
649
650 defp restrict_local(query, %{"local_only" => true}) do
651 from(activity in query, where: activity.local == true)
652 end
653
654 defp restrict_local(query, _), do: query
655
656 defp restrict_actor(query, %{"actor_id" => actor_id}) do
657 from(activity in query, where: activity.actor == ^actor_id)
658 end
659
660 defp restrict_actor(query, _), do: query
661
662 defp restrict_type(query, %{"type" => type}) when is_binary(type) do
663 from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
664 end
665
666 defp restrict_type(query, %{"type" => type}) do
667 from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
668 end
669
670 defp restrict_type(query, _), do: query
671
672 defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
673 from(
674 activity in query,
675 where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
676 )
677 end
678
679 defp restrict_favorited_by(query, _), do: query
680
681 defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
682 raise "Can't use the child object without preloading!"
683 end
684
685 defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
686 from(
687 [_activity, object] in query,
688 where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
689 )
690 end
691
692 defp restrict_media(query, _), do: query
693
694 defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
695 from(
696 activity in query,
697 where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
698 )
699 end
700
701 defp restrict_replies(query, _), do: query
702
703 defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
704 from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
705 end
706
707 defp restrict_reblogs(query, _), do: query
708
709 defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
710
711 defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
712 mutes = info.mutes
713
714 from(
715 activity in query,
716 where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
717 where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
718 )
719 end
720
721 defp restrict_muted(query, _), do: query
722
723 defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
724 blocks = info.blocks || []
725 domain_blocks = info.domain_blocks || []
726
727 from(
728 activity in query,
729 where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
730 where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks),
731 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
732 )
733 end
734
735 defp restrict_blocked(query, _), do: query
736
737 defp restrict_unlisted(query) do
738 from(
739 activity in query,
740 where:
741 fragment(
742 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
743 activity.data,
744 ^["https://www.w3.org/ns/activitystreams#Public"]
745 )
746 )
747 end
748
749 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
750 from(activity in query, where: activity.id in ^ids)
751 end
752
753 defp restrict_pinned(query, _), do: query
754
755 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
756 muted_reblogs = info.muted_reblogs || []
757
758 from(
759 activity in query,
760 where:
761 fragment(
762 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
763 activity.data,
764 activity.actor,
765 ^muted_reblogs
766 )
767 )
768 end
769
770 defp restrict_muted_reblogs(query, _), do: query
771
772 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
773
774 defp maybe_preload_objects(query, _) do
775 query
776 |> Activity.with_preloaded_object()
777 end
778
779 def fetch_activities_query(recipients, opts \\ %{}) do
780 base_query = from(activity in Activity)
781
782 base_query
783 |> maybe_preload_objects(opts)
784 |> restrict_recipients(recipients, opts["user"])
785 |> restrict_tag(opts)
786 |> restrict_tag_reject(opts)
787 |> restrict_tag_all(opts)
788 |> restrict_since(opts)
789 |> restrict_local(opts)
790 |> restrict_actor(opts)
791 |> restrict_type(opts)
792 |> restrict_favorited_by(opts)
793 |> restrict_blocked(opts)
794 |> restrict_muted(opts)
795 |> restrict_media(opts)
796 |> restrict_visibility(opts)
797 |> restrict_replies(opts)
798 |> restrict_reblogs(opts)
799 |> restrict_pinned(opts)
800 |> restrict_muted_reblogs(opts)
801 end
802
803 def fetch_activities(recipients, opts \\ %{}) do
804 fetch_activities_query(recipients, opts)
805 |> Pagination.fetch_paginated(opts)
806 |> Enum.reverse()
807 end
808
809 def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
810 fetch_activities_query([], opts)
811 |> restrict_to_cc(recipients_to, recipients_cc)
812 |> Pagination.fetch_paginated(opts)
813 |> Enum.reverse()
814 end
815
816 def upload(file, opts \\ []) do
817 with {:ok, data} <- Upload.store(file, opts) do
818 obj_data =
819 if opts[:actor] do
820 Map.put(data, "actor", opts[:actor])
821 else
822 data
823 end
824
825 Repo.insert(%Object{data: obj_data})
826 end
827 end
828
829 def user_data_from_user_object(data) do
830 avatar =
831 data["icon"]["url"] &&
832 %{
833 "type" => "Image",
834 "url" => [%{"href" => data["icon"]["url"]}]
835 }
836
837 banner =
838 data["image"]["url"] &&
839 %{
840 "type" => "Image",
841 "url" => [%{"href" => data["image"]["url"]}]
842 }
843
844 locked = data["manuallyApprovesFollowers"] || false
845 data = Transmogrifier.maybe_fix_user_object(data)
846
847 user_data = %{
848 ap_id: data["id"],
849 info: %{
850 "ap_enabled" => true,
851 "source_data" => data,
852 "banner" => banner,
853 "locked" => locked
854 },
855 avatar: avatar,
856 name: data["name"],
857 follower_address: data["followers"],
858 bio: data["summary"]
859 }
860
861 # nickname can be nil because of virtual actors
862 user_data =
863 if data["preferredUsername"] do
864 Map.put(
865 user_data,
866 :nickname,
867 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
868 )
869 else
870 Map.put(user_data, :nickname, nil)
871 end
872
873 {:ok, user_data}
874 end
875
876 def fetch_and_prepare_user_from_ap_id(ap_id) do
877 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
878 user_data_from_user_object(data)
879 else
880 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
881 end
882 end
883
884 def make_user_from_ap_id(ap_id) do
885 if _user = User.get_by_ap_id(ap_id) do
886 Transmogrifier.upgrade_user_from_ap_id(ap_id)
887 else
888 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
889 User.insert_or_update_user(data)
890 else
891 e -> {:error, e}
892 end
893 end
894 end
895
896 def make_user_from_nickname(nickname) do
897 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
898 make_user_from_ap_id(ap_id)
899 else
900 _e -> {:error, "No AP id in WebFinger"}
901 end
902 end
903
904 def should_federate?(inbox, public) do
905 if public do
906 true
907 else
908 inbox_info = URI.parse(inbox)
909 !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
910 end
911 end
912
913 def publish(actor, activity) do
914 remote_followers =
915 if actor.follower_address in activity.recipients do
916 {:ok, followers} = User.get_followers(actor)
917 followers |> Enum.filter(&(!&1.local))
918 else
919 []
920 end
921
922 public = is_public?(activity)
923
924 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
925 json = Jason.encode!(data)
926
927 (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
928 |> Enum.filter(fn user -> User.ap_enabled?(user) end)
929 |> Enum.map(fn %{info: %{source_data: data}} ->
930 (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
931 end)
932 |> Enum.uniq()
933 |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
934 |> Instances.filter_reachable()
935 |> Enum.each(fn {inbox, unreachable_since} ->
936 Federator.publish_single_ap(%{
937 inbox: inbox,
938 json: json,
939 actor: actor,
940 id: activity.data["id"],
941 unreachable_since: unreachable_since
942 })
943 end)
944 end
945
946 def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do
947 Logger.info("Federating #{id} to #{inbox}")
948 host = URI.parse(inbox).host
949
950 digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
951
952 date =
953 NaiveDateTime.utc_now()
954 |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
955
956 signature =
957 Pleroma.Web.HTTPSignatures.sign(actor, %{
958 host: host,
959 "content-length": byte_size(json),
960 digest: digest,
961 date: date
962 })
963
964 with {:ok, %{status: code}} when code in 200..299 <-
965 result =
966 @httpoison.post(
967 inbox,
968 json,
969 [
970 {"Content-Type", "application/activity+json"},
971 {"Date", date},
972 {"signature", signature},
973 {"digest", digest}
974 ]
975 ) do
976 if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
977 do: Instances.set_reachable(inbox)
978
979 result
980 else
981 {_post_result, response} ->
982 unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
983 {:error, response}
984 end
985 end
986
987 # filter out broken threads
988 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
989 entire_thread_visible_for_user?(activity, user)
990 end
991
992 # do post-processing on a specific activity
993 def contain_activity(%Activity{} = activity, %User{} = user) do
994 contain_broken_threads(activity, user)
995 end
996
997 # do post-processing on a timeline
998 def contain_timeline(timeline, user) do
999 timeline
1000 |> Enum.filter(fn activity ->
1001 contain_activity(activity, user)
1002 end)
1003 end
1004 end