Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
[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", "Delete"] 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 (? && ?)", activity.recipients, ^blocks),
731 where:
732 fragment(
733 "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
734 activity.data,
735 activity.data,
736 ^blocks
737 ),
738 where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
739 )
740 end
741
742 defp restrict_blocked(query, _), do: query
743
744 defp restrict_unlisted(query) do
745 from(
746 activity in query,
747 where:
748 fragment(
749 "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
750 activity.data,
751 ^["https://www.w3.org/ns/activitystreams#Public"]
752 )
753 )
754 end
755
756 defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
757 from(activity in query, where: activity.id in ^ids)
758 end
759
760 defp restrict_pinned(query, _), do: query
761
762 defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
763 muted_reblogs = info.muted_reblogs || []
764
765 from(
766 activity in query,
767 where:
768 fragment(
769 "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
770 activity.data,
771 activity.actor,
772 ^muted_reblogs
773 )
774 )
775 end
776
777 defp restrict_muted_reblogs(query, _), do: query
778
779 defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
780
781 defp maybe_preload_objects(query, _) do
782 query
783 |> Activity.with_preloaded_object()
784 end
785
786 def fetch_activities_query(recipients, opts \\ %{}) do
787 base_query = from(activity in Activity)
788
789 base_query
790 |> maybe_preload_objects(opts)
791 |> restrict_recipients(recipients, opts["user"])
792 |> restrict_tag(opts)
793 |> restrict_tag_reject(opts)
794 |> restrict_tag_all(opts)
795 |> restrict_since(opts)
796 |> restrict_local(opts)
797 |> restrict_actor(opts)
798 |> restrict_type(opts)
799 |> restrict_favorited_by(opts)
800 |> restrict_blocked(opts)
801 |> restrict_muted(opts)
802 |> restrict_media(opts)
803 |> restrict_visibility(opts)
804 |> restrict_replies(opts)
805 |> restrict_reblogs(opts)
806 |> restrict_pinned(opts)
807 |> restrict_muted_reblogs(opts)
808 |> Activity.restrict_deactivated_users()
809 end
810
811 def fetch_activities(recipients, opts \\ %{}) do
812 fetch_activities_query(recipients, opts)
813 |> Pagination.fetch_paginated(opts)
814 |> Enum.reverse()
815 end
816
817 def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
818 fetch_activities_query([], opts)
819 |> restrict_to_cc(recipients_to, recipients_cc)
820 |> Pagination.fetch_paginated(opts)
821 |> Enum.reverse()
822 end
823
824 def upload(file, opts \\ []) do
825 with {:ok, data} <- Upload.store(file, opts) do
826 obj_data =
827 if opts[:actor] do
828 Map.put(data, "actor", opts[:actor])
829 else
830 data
831 end
832
833 Repo.insert(%Object{data: obj_data})
834 end
835 end
836
837 def user_data_from_user_object(data) do
838 avatar =
839 data["icon"]["url"] &&
840 %{
841 "type" => "Image",
842 "url" => [%{"href" => data["icon"]["url"]}]
843 }
844
845 banner =
846 data["image"]["url"] &&
847 %{
848 "type" => "Image",
849 "url" => [%{"href" => data["image"]["url"]}]
850 }
851
852 locked = data["manuallyApprovesFollowers"] || false
853 data = Transmogrifier.maybe_fix_user_object(data)
854
855 user_data = %{
856 ap_id: data["id"],
857 info: %{
858 "ap_enabled" => true,
859 "source_data" => data,
860 "banner" => banner,
861 "locked" => locked
862 },
863 avatar: avatar,
864 name: data["name"],
865 follower_address: data["followers"],
866 bio: data["summary"]
867 }
868
869 # nickname can be nil because of virtual actors
870 user_data =
871 if data["preferredUsername"] do
872 Map.put(
873 user_data,
874 :nickname,
875 "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
876 )
877 else
878 Map.put(user_data, :nickname, nil)
879 end
880
881 {:ok, user_data}
882 end
883
884 def fetch_and_prepare_user_from_ap_id(ap_id) do
885 with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
886 user_data_from_user_object(data)
887 else
888 e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
889 end
890 end
891
892 def make_user_from_ap_id(ap_id) do
893 if _user = User.get_by_ap_id(ap_id) do
894 Transmogrifier.upgrade_user_from_ap_id(ap_id)
895 else
896 with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
897 User.insert_or_update_user(data)
898 else
899 e -> {:error, e}
900 end
901 end
902 end
903
904 def make_user_from_nickname(nickname) do
905 with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
906 make_user_from_ap_id(ap_id)
907 else
908 _e -> {:error, "No AP id in WebFinger"}
909 end
910 end
911
912 def should_federate?(inbox, public) do
913 if public do
914 true
915 else
916 inbox_info = URI.parse(inbox)
917 !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
918 end
919 end
920
921 def publish(actor, activity) do
922 remote_followers =
923 if actor.follower_address in activity.recipients do
924 {:ok, followers} = User.get_followers(actor)
925 followers |> Enum.filter(&(!&1.local))
926 else
927 []
928 end
929
930 public = is_public?(activity)
931
932 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
933 json = Jason.encode!(data)
934
935 (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
936 |> Enum.filter(fn user -> User.ap_enabled?(user) end)
937 |> Enum.map(fn %{info: %{source_data: data}} ->
938 (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
939 end)
940 |> Enum.uniq()
941 |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
942 |> Instances.filter_reachable()
943 |> Enum.each(fn {inbox, unreachable_since} ->
944 Federator.publish_single_ap(%{
945 inbox: inbox,
946 json: json,
947 actor: actor,
948 id: activity.data["id"],
949 unreachable_since: unreachable_since
950 })
951 end)
952 end
953
954 def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do
955 Logger.info("Federating #{id} to #{inbox}")
956 host = URI.parse(inbox).host
957
958 digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
959
960 date =
961 NaiveDateTime.utc_now()
962 |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
963
964 signature =
965 Pleroma.Web.HTTPSignatures.sign(actor, %{
966 host: host,
967 "content-length": byte_size(json),
968 digest: digest,
969 date: date
970 })
971
972 with {:ok, %{status: code}} when code in 200..299 <-
973 result =
974 @httpoison.post(
975 inbox,
976 json,
977 [
978 {"Content-Type", "application/activity+json"},
979 {"Date", date},
980 {"signature", signature},
981 {"digest", digest}
982 ]
983 ) do
984 if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
985 do: Instances.set_reachable(inbox)
986
987 result
988 else
989 {_post_result, response} ->
990 unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
991 {:error, response}
992 end
993 end
994
995 # filter out broken threads
996 def contain_broken_threads(%Activity{} = activity, %User{} = user) do
997 entire_thread_visible_for_user?(activity, user)
998 end
999
1000 # do post-processing on a specific activity
1001 def contain_activity(%Activity{} = activity, %User{} = user) do
1002 contain_broken_threads(activity, user)
1003 end
1004
1005 # do post-processing on a timeline
1006 def contain_timeline(timeline, user) do
1007 timeline
1008 |> Enum.filter(fn activity ->
1009 contain_activity(activity, user)
1010 end)
1011 end
1012 end