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