Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1697 lines
50KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.ActivityPub do
  5. alias Pleroma.Activity
  6. alias Pleroma.Activity.Ir.Topics
  7. alias Pleroma.Config
  8. alias Pleroma.Constants
  9. alias Pleroma.Conversation
  10. alias Pleroma.Conversation.Participation
  11. alias Pleroma.Filter
  12. alias Pleroma.Hashtag
  13. alias Pleroma.Maps
  14. alias Pleroma.Notification
  15. alias Pleroma.Object
  16. alias Pleroma.Object.Containment
  17. alias Pleroma.Object.Fetcher
  18. alias Pleroma.Pagination
  19. alias Pleroma.Repo
  20. alias Pleroma.Upload
  21. alias Pleroma.User
  22. alias Pleroma.Web.ActivityPub.MRF
  23. alias Pleroma.Web.ActivityPub.Transmogrifier
  24. alias Pleroma.Web.Streamer
  25. alias Pleroma.Web.WebFinger
  26. alias Pleroma.Workers.BackgroundWorker
  27. import Ecto.Query
  28. import Pleroma.Web.ActivityPub.Utils
  29. import Pleroma.Web.ActivityPub.Visibility
  30. require Logger
  31. require Pleroma.Constants
  32. @behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
  33. @behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming
  34. defp get_recipients(%{"type" => "Create"} = data) do
  35. to = Map.get(data, "to", [])
  36. cc = Map.get(data, "cc", [])
  37. bcc = Map.get(data, "bcc", [])
  38. actor = Map.get(data, "actor", [])
  39. recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
  40. {recipients, to, cc}
  41. end
  42. defp get_recipients(data) do
  43. to = Map.get(data, "to", [])
  44. cc = Map.get(data, "cc", [])
  45. bcc = Map.get(data, "bcc", [])
  46. recipients = Enum.concat([to, cc, bcc])
  47. {recipients, to, cc}
  48. end
  49. defp check_actor_can_insert(%{"type" => "Delete"}), do: true
  50. defp check_actor_can_insert(%{"type" => "Undo"}), do: true
  51. defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do
  52. case User.get_cached_by_ap_id(actor) do
  53. %User{is_active: true} -> true
  54. _ -> false
  55. end
  56. end
  57. defp check_actor_can_insert(_), do: true
  58. defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
  59. limit = Config.get([:instance, :remote_limit])
  60. String.length(content) <= limit
  61. end
  62. defp check_remote_limit(_), do: true
  63. def increase_note_count_if_public(actor, object) do
  64. if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
  65. end
  66. def decrease_note_count_if_public(actor, object) do
  67. if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
  68. end
  69. defp increase_replies_count_if_reply(%{
  70. "object" => %{"inReplyTo" => reply_ap_id} = object,
  71. "type" => "Create"
  72. }) do
  73. if is_public?(object) do
  74. Object.increase_replies_count(reply_ap_id)
  75. end
  76. end
  77. defp increase_replies_count_if_reply(_create_data), do: :noop
  78. @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page]
  79. @impl true
  80. def persist(%{"type" => type} = object, meta) when type in @object_types do
  81. with {:ok, object} <- Object.create(object) do
  82. {:ok, object, meta}
  83. end
  84. end
  85. @impl true
  86. def persist(object, meta) do
  87. with local <- Keyword.fetch!(meta, :local),
  88. {recipients, _, _} <- get_recipients(object),
  89. {:ok, activity} <-
  90. Repo.insert(%Activity{
  91. data: object,
  92. local: local,
  93. recipients: recipients,
  94. actor: object["actor"]
  95. }),
  96. # TODO: add tests for expired activities, when Note type will be supported in new pipeline
  97. {:ok, _} <- maybe_create_activity_expiration(activity) do
  98. {:ok, activity, meta}
  99. end
  100. end
  101. @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  102. def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
  103. with nil <- Activity.normalize(map),
  104. map <- lazy_put_activity_defaults(map, fake),
  105. {_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)},
  106. {_, true} <- {:remote_limit_pass, check_remote_limit(map)},
  107. {:ok, map} <- MRF.filter(map),
  108. {recipients, _, _} = get_recipients(map),
  109. {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
  110. {:containment, :ok} <- {:containment, Containment.contain_child(map)},
  111. {:ok, map, object} <- insert_full_object(map),
  112. {:ok, activity} <- insert_activity_with_expiration(map, local, recipients) do
  113. # Splice in the child object if we have one.
  114. activity = Maps.put_if_present(activity, :object, object)
  115. ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
  116. Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
  117. end)
  118. {:ok, activity}
  119. else
  120. %Activity{} = activity ->
  121. {:ok, activity}
  122. {:actor_check, _} ->
  123. {:error, false}
  124. {:containment, _} = error ->
  125. error
  126. {:error, _} = error ->
  127. error
  128. {:fake, true, map, recipients} ->
  129. activity = %Activity{
  130. data: map,
  131. local: local,
  132. actor: map["actor"],
  133. recipients: recipients,
  134. id: "pleroma:fakeid"
  135. }
  136. Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
  137. {:ok, activity}
  138. {:remote_limit_pass, _} ->
  139. {:error, :remote_limit}
  140. {:reject, _} = e ->
  141. {:error, e}
  142. end
  143. end
  144. defp insert_activity_with_expiration(data, local, recipients) do
  145. struct = %Activity{
  146. data: data,
  147. local: local,
  148. actor: data["actor"],
  149. recipients: recipients
  150. }
  151. with {:ok, activity} <- Repo.insert(struct) do
  152. maybe_create_activity_expiration(activity)
  153. end
  154. end
  155. def notify_and_stream(activity) do
  156. Notification.create_notifications(activity)
  157. conversation = create_or_bump_conversation(activity, activity.actor)
  158. participations = get_participations(conversation)
  159. stream_out(activity)
  160. stream_out_participations(participations)
  161. end
  162. defp maybe_create_activity_expiration(
  163. %{data: %{"expires_at" => %DateTime{} = expires_at}} = activity
  164. ) do
  165. with {:ok, _job} <-
  166. Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
  167. activity_id: activity.id,
  168. expires_at: expires_at
  169. }) do
  170. {:ok, activity}
  171. end
  172. end
  173. defp maybe_create_activity_expiration(activity), do: {:ok, activity}
  174. defp create_or_bump_conversation(activity, actor) do
  175. with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
  176. %User{} = user <- User.get_cached_by_ap_id(actor) do
  177. Participation.mark_as_read(user, conversation)
  178. {:ok, conversation}
  179. end
  180. end
  181. defp get_participations({:ok, conversation}) do
  182. conversation
  183. |> Repo.preload(:participations, force: true)
  184. |> Map.get(:participations)
  185. end
  186. defp get_participations(_), do: []
  187. def stream_out_participations(participations) do
  188. participations =
  189. participations
  190. |> Repo.preload(:user)
  191. Streamer.stream("participation", participations)
  192. end
  193. @impl true
  194. def stream_out_participations(%Object{data: %{"context" => context}}, user) do
  195. with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
  196. conversation = Repo.preload(conversation, :participations)
  197. last_activity_id =
  198. fetch_latest_direct_activity_id_for_context(conversation.ap_id, %{
  199. user: user,
  200. blocking_user: user
  201. })
  202. if last_activity_id do
  203. stream_out_participations(conversation.participations)
  204. end
  205. end
  206. end
  207. @impl true
  208. def stream_out_participations(_, _), do: :noop
  209. @impl true
  210. def stream_out(%Activity{data: %{"type" => data_type}} = activity)
  211. when data_type in ["Create", "Announce", "Delete"] do
  212. activity
  213. |> Topics.get_activity_topics()
  214. |> Streamer.stream(activity)
  215. end
  216. @impl true
  217. def stream_out(_activity) do
  218. :noop
  219. end
  220. @spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  221. def create(params, fake \\ false) do
  222. with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
  223. result
  224. end
  225. end
  226. defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
  227. additional = params[:additional] || %{}
  228. # only accept false as false value
  229. local = !(params[:local] == false)
  230. published = params[:published]
  231. quick_insert? = Config.get([:env]) == :benchmark
  232. create_data =
  233. make_create_data(
  234. %{to: to, actor: actor, published: published, context: context, object: object},
  235. additional
  236. )
  237. with {:ok, activity} <- insert(create_data, local, fake),
  238. {:fake, false, activity} <- {:fake, fake, activity},
  239. _ <- increase_replies_count_if_reply(create_data),
  240. {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
  241. {:ok, _actor} <- increase_note_count_if_public(actor, activity),
  242. _ <- notify_and_stream(activity),
  243. :ok <- maybe_federate(activity) do
  244. {:ok, activity}
  245. else
  246. {:quick_insert, true, activity} ->
  247. {:ok, activity}
  248. {:fake, true, activity} ->
  249. {:ok, activity}
  250. {:error, message} ->
  251. Repo.rollback(message)
  252. end
  253. end
  254. @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
  255. def listen(%{to: to, actor: actor, context: context, object: object} = params) do
  256. additional = params[:additional] || %{}
  257. # only accept false as false value
  258. local = !(params[:local] == false)
  259. published = params[:published]
  260. listen_data =
  261. make_listen_data(
  262. %{to: to, actor: actor, published: published, context: context, object: object},
  263. additional
  264. )
  265. with {:ok, activity} <- insert(listen_data, local),
  266. _ <- notify_and_stream(activity),
  267. :ok <- maybe_federate(activity) do
  268. {:ok, activity}
  269. end
  270. end
  271. @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
  272. {:ok, Activity.t()} | nil | {:error, any()}
  273. def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
  274. with {:ok, result} <-
  275. Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
  276. result
  277. end
  278. end
  279. defp do_unfollow(follower, followed, activity_id, local) do
  280. with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
  281. {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
  282. unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
  283. {:ok, activity} <- insert(unfollow_data, local),
  284. _ <- notify_and_stream(activity),
  285. :ok <- maybe_federate(activity) do
  286. {:ok, activity}
  287. else
  288. nil -> nil
  289. {:error, error} -> Repo.rollback(error)
  290. end
  291. end
  292. @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
  293. def flag(params) do
  294. with {:ok, result} <- Repo.transaction(fn -> do_flag(params) end) do
  295. result
  296. end
  297. end
  298. defp do_flag(
  299. %{
  300. actor: actor,
  301. context: _context,
  302. account: account,
  303. statuses: statuses,
  304. content: content
  305. } = params
  306. ) do
  307. # only accept false as false value
  308. local = !(params[:local] == false)
  309. forward = !(params[:forward] == false)
  310. additional = params[:additional] || %{}
  311. additional =
  312. if forward do
  313. Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
  314. else
  315. Map.merge(additional, %{"to" => [], "cc" => []})
  316. end
  317. with flag_data <- make_flag_data(params, additional),
  318. {:ok, activity} <- insert(flag_data, local),
  319. {:ok, stripped_activity} <- strip_report_status_data(activity),
  320. _ <- notify_and_stream(activity),
  321. :ok <-
  322. maybe_federate(stripped_activity) do
  323. User.all_superusers()
  324. |> Enum.filter(fn user -> user.ap_id != actor end)
  325. |> Enum.filter(fn user -> not is_nil(user.email) end)
  326. |> Enum.each(fn superuser ->
  327. superuser
  328. |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
  329. |> Pleroma.Emails.Mailer.deliver_async()
  330. end)
  331. {:ok, activity}
  332. else
  333. {:error, error} -> Repo.rollback(error)
  334. end
  335. end
  336. @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  337. def move(%User{} = origin, %User{} = target, local \\ true) do
  338. params = %{
  339. "type" => "Move",
  340. "actor" => origin.ap_id,
  341. "object" => origin.ap_id,
  342. "target" => target.ap_id
  343. }
  344. with true <- origin.ap_id in target.also_known_as,
  345. {:ok, activity} <- insert(params, local),
  346. _ <- notify_and_stream(activity) do
  347. maybe_federate(activity)
  348. BackgroundWorker.enqueue("move_following", %{
  349. "origin_id" => origin.id,
  350. "target_id" => target.id
  351. })
  352. {:ok, activity}
  353. else
  354. false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
  355. err -> err
  356. end
  357. end
  358. def fetch_activities_for_context_query(context, opts) do
  359. public = [Constants.as_public()]
  360. recipients =
  361. if opts[:user],
  362. do: [opts[:user].ap_id | User.following(opts[:user])] ++ public,
  363. else: public
  364. from(activity in Activity)
  365. |> maybe_preload_objects(opts)
  366. |> maybe_preload_bookmarks(opts)
  367. |> maybe_set_thread_muted_field(opts)
  368. |> restrict_blocked(opts)
  369. |> restrict_recipients(recipients, opts[:user])
  370. |> restrict_filtered(opts)
  371. |> where(
  372. [activity],
  373. fragment(
  374. "?->>'type' = ? and ?->>'context' = ?",
  375. activity.data,
  376. "Create",
  377. activity.data,
  378. ^context
  379. )
  380. )
  381. |> exclude_poll_votes(opts)
  382. |> exclude_id(opts)
  383. |> order_by([activity], desc: activity.id)
  384. end
  385. @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
  386. def fetch_activities_for_context(context, opts \\ %{}) do
  387. context
  388. |> fetch_activities_for_context_query(opts)
  389. |> Repo.all()
  390. end
  391. @spec fetch_latest_direct_activity_id_for_context(String.t(), keyword() | map()) ::
  392. FlakeId.Ecto.CompatType.t() | nil
  393. def fetch_latest_direct_activity_id_for_context(context, opts \\ %{}) do
  394. context
  395. |> fetch_activities_for_context_query(Map.merge(%{skip_preload: true}, opts))
  396. |> restrict_visibility(%{visibility: "direct"})
  397. |> limit(1)
  398. |> select([a], a.id)
  399. |> Repo.one()
  400. end
  401. defp fetch_paginated_optimized(query, opts, pagination) do
  402. # Note: tag-filtering funcs may apply "ORDER BY objects.id DESC",
  403. # and extra sorting on "activities.id DESC NULLS LAST" would worse the query plan
  404. opts = Map.put(opts, :skip_extra_order, true)
  405. Pagination.fetch_paginated(query, opts, pagination)
  406. end
  407. def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
  408. list_memberships = Pleroma.List.memberships(opts[:user])
  409. fetch_activities_query(recipients ++ list_memberships, opts)
  410. |> fetch_paginated_optimized(opts, pagination)
  411. |> Enum.reverse()
  412. |> maybe_update_cc(list_memberships, opts[:user])
  413. end
  414. @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
  415. def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
  416. opts = Map.delete(opts, :user)
  417. [Constants.as_public()]
  418. |> fetch_activities_query(opts)
  419. |> restrict_unlisted(opts)
  420. |> fetch_paginated_optimized(opts, pagination)
  421. end
  422. @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
  423. def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
  424. opts
  425. |> Map.put(:restrict_unlisted, true)
  426. |> fetch_public_or_unlisted_activities(pagination)
  427. end
  428. @valid_visibilities ~w[direct unlisted public private]
  429. defp restrict_visibility(query, %{visibility: visibility})
  430. when is_list(visibility) do
  431. if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
  432. from(
  433. a in query,
  434. where:
  435. fragment(
  436. "activity_visibility(?, ?, ?) = ANY (?)",
  437. a.actor,
  438. a.recipients,
  439. a.data,
  440. ^visibility
  441. )
  442. )
  443. else
  444. Logger.error("Could not restrict visibility to #{visibility}")
  445. end
  446. end
  447. defp restrict_visibility(query, %{visibility: visibility})
  448. when visibility in @valid_visibilities do
  449. from(
  450. a in query,
  451. where:
  452. fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
  453. )
  454. end
  455. defp restrict_visibility(_query, %{visibility: visibility})
  456. when visibility not in @valid_visibilities do
  457. Logger.error("Could not restrict visibility to #{visibility}")
  458. end
  459. defp restrict_visibility(query, _visibility), do: query
  460. defp exclude_visibility(query, %{exclude_visibilities: visibility})
  461. when is_list(visibility) do
  462. if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
  463. from(
  464. a in query,
  465. where:
  466. not fragment(
  467. "activity_visibility(?, ?, ?) = ANY (?)",
  468. a.actor,
  469. a.recipients,
  470. a.data,
  471. ^visibility
  472. )
  473. )
  474. else
  475. Logger.error("Could not exclude visibility to #{visibility}")
  476. query
  477. end
  478. end
  479. defp exclude_visibility(query, %{exclude_visibilities: visibility})
  480. when visibility in @valid_visibilities do
  481. from(
  482. a in query,
  483. where:
  484. not fragment(
  485. "activity_visibility(?, ?, ?) = ?",
  486. a.actor,
  487. a.recipients,
  488. a.data,
  489. ^visibility
  490. )
  491. )
  492. end
  493. defp exclude_visibility(query, %{exclude_visibilities: visibility})
  494. when visibility not in [nil | @valid_visibilities] do
  495. Logger.error("Could not exclude visibility to #{visibility}")
  496. query
  497. end
  498. defp exclude_visibility(query, _visibility), do: query
  499. defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
  500. do: query
  501. defp restrict_thread_visibility(query, %{user: %User{skip_thread_containment: true}}, _),
  502. do: query
  503. defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do
  504. from(
  505. a in query,
  506. where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
  507. )
  508. end
  509. defp restrict_thread_visibility(query, _, _), do: query
  510. def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
  511. params =
  512. params
  513. |> Map.put(:user, reading_user)
  514. |> Map.put(:actor_id, user.ap_id)
  515. %{
  516. godmode: params[:godmode],
  517. reading_user: reading_user
  518. }
  519. |> user_activities_recipients()
  520. |> fetch_activities(params)
  521. |> Enum.reverse()
  522. end
  523. def fetch_user_activities(user, reading_user, params \\ %{})
  524. def fetch_user_activities(user, reading_user, %{total: true} = params) do
  525. result = fetch_activities_for_user(user, reading_user, params)
  526. Keyword.put(result, :items, Enum.reverse(result[:items]))
  527. end
  528. def fetch_user_activities(user, reading_user, params) do
  529. user
  530. |> fetch_activities_for_user(reading_user, params)
  531. |> Enum.reverse()
  532. end
  533. defp fetch_activities_for_user(user, reading_user, params) do
  534. params =
  535. params
  536. |> Map.put(:type, ["Create", "Announce"])
  537. |> Map.put(:user, reading_user)
  538. |> Map.put(:actor_id, user.ap_id)
  539. |> Map.put(:pinned_object_ids, Map.keys(user.pinned_objects))
  540. params =
  541. if User.blocks?(reading_user, user) do
  542. params
  543. else
  544. params
  545. |> Map.put(:blocking_user, reading_user)
  546. |> Map.put(:muting_user, reading_user)
  547. end
  548. pagination_type = Map.get(params, :pagination_type) || :keyset
  549. %{
  550. godmode: params[:godmode],
  551. reading_user: reading_user
  552. }
  553. |> user_activities_recipients()
  554. |> fetch_activities(params, pagination_type)
  555. end
  556. def fetch_statuses(reading_user, %{total: true} = params) do
  557. result = fetch_activities_for_reading_user(reading_user, params)
  558. Keyword.put(result, :items, Enum.reverse(result[:items]))
  559. end
  560. def fetch_statuses(reading_user, params) do
  561. reading_user
  562. |> fetch_activities_for_reading_user(params)
  563. |> Enum.reverse()
  564. end
  565. defp fetch_activities_for_reading_user(reading_user, params) do
  566. params = Map.put(params, :type, ["Create", "Announce"])
  567. %{
  568. godmode: params[:godmode],
  569. reading_user: reading_user
  570. }
  571. |> user_activities_recipients()
  572. |> fetch_activities(params, :offset)
  573. end
  574. defp user_activities_recipients(%{godmode: true}), do: []
  575. defp user_activities_recipients(%{reading_user: reading_user}) do
  576. if reading_user do
  577. [Constants.as_public(), reading_user.ap_id | User.following(reading_user)]
  578. else
  579. [Constants.as_public()]
  580. end
  581. end
  582. defp restrict_announce_object_actor(_query, %{announce_filtering_user: _, skip_preload: true}) do
  583. raise "Can't use the child object without preloading!"
  584. end
  585. defp restrict_announce_object_actor(query, %{announce_filtering_user: %{ap_id: actor}}) do
  586. from(
  587. [activity, object] in query,
  588. where:
  589. fragment(
  590. "?->>'type' != ? or ?->>'actor' != ?",
  591. activity.data,
  592. "Announce",
  593. object.data,
  594. ^actor
  595. )
  596. )
  597. end
  598. defp restrict_announce_object_actor(query, _), do: query
  599. defp restrict_since(query, %{since_id: ""}), do: query
  600. defp restrict_since(query, %{since_id: since_id}) do
  601. from(activity in query, where: activity.id > ^since_id)
  602. end
  603. defp restrict_since(query, _), do: query
  604. defp restrict_embedded_tag_all(_query, %{tag_all: _tag_all, skip_preload: true}) do
  605. raise_on_missing_preload()
  606. end
  607. defp restrict_embedded_tag_all(query, %{tag_all: [_ | _] = tag_all}) do
  608. from(
  609. [_activity, object] in query,
  610. where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
  611. )
  612. end
  613. defp restrict_embedded_tag_all(query, %{tag_all: tag}) when is_binary(tag) do
  614. restrict_embedded_tag_any(query, %{tag: tag})
  615. end
  616. defp restrict_embedded_tag_all(query, _), do: query
  617. defp restrict_embedded_tag_any(_query, %{tag: _tag, skip_preload: true}) do
  618. raise_on_missing_preload()
  619. end
  620. defp restrict_embedded_tag_any(query, %{tag: [_ | _] = tag_any}) do
  621. from(
  622. [_activity, object] in query,
  623. where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag_any)
  624. )
  625. end
  626. defp restrict_embedded_tag_any(query, %{tag: tag}) when is_binary(tag) do
  627. restrict_embedded_tag_any(query, %{tag: [tag]})
  628. end
  629. defp restrict_embedded_tag_any(query, _), do: query
  630. defp restrict_embedded_tag_reject_any(_query, %{tag_reject: _tag_reject, skip_preload: true}) do
  631. raise_on_missing_preload()
  632. end
  633. defp restrict_embedded_tag_reject_any(query, %{tag_reject: [_ | _] = tag_reject}) do
  634. from(
  635. [_activity, object] in query,
  636. where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
  637. )
  638. end
  639. defp restrict_embedded_tag_reject_any(query, %{tag_reject: tag_reject})
  640. when is_binary(tag_reject) do
  641. restrict_embedded_tag_reject_any(query, %{tag_reject: [tag_reject]})
  642. end
  643. defp restrict_embedded_tag_reject_any(query, _), do: query
  644. defp object_ids_query_for_tags(tags) do
  645. from(hto in "hashtags_objects")
  646. |> join(:inner, [hto], ht in Pleroma.Hashtag, on: hto.hashtag_id == ht.id)
  647. |> where([hto, ht], ht.name in ^tags)
  648. |> select([hto], hto.object_id)
  649. |> distinct([hto], true)
  650. end
  651. defp restrict_hashtag_all(_query, %{tag_all: _tag, skip_preload: true}) do
  652. raise_on_missing_preload()
  653. end
  654. defp restrict_hashtag_all(query, %{tag_all: [single_tag]}) do
  655. restrict_hashtag_any(query, %{tag: single_tag})
  656. end
  657. defp restrict_hashtag_all(query, %{tag_all: [_ | _] = tags}) do
  658. from(
  659. [_activity, object] in query,
  660. where:
  661. fragment(
  662. """
  663. (SELECT array_agg(hashtags.name) FROM hashtags JOIN hashtags_objects
  664. ON hashtags_objects.hashtag_id = hashtags.id WHERE hashtags.name = ANY(?)
  665. AND hashtags_objects.object_id = ?) @> ?
  666. """,
  667. ^tags,
  668. object.id,
  669. ^tags
  670. )
  671. )
  672. end
  673. defp restrict_hashtag_all(query, %{tag_all: tag}) when is_binary(tag) do
  674. restrict_hashtag_all(query, %{tag_all: [tag]})
  675. end
  676. defp restrict_hashtag_all(query, _), do: query
  677. defp restrict_hashtag_any(_query, %{tag: _tag, skip_preload: true}) do
  678. raise_on_missing_preload()
  679. end
  680. defp restrict_hashtag_any(query, %{tag: [_ | _] = tags}) do
  681. hashtag_ids =
  682. from(ht in Hashtag, where: ht.name in ^tags, select: ht.id)
  683. |> Repo.all()
  684. # Note: NO extra ordering should be done on "activities.id desc nulls last" for optimal plan
  685. from(
  686. [_activity, object] in query,
  687. join: hto in "hashtags_objects",
  688. on: hto.object_id == object.id,
  689. where: hto.hashtag_id in ^hashtag_ids,
  690. distinct: [desc: object.id],
  691. order_by: [desc: object.id]
  692. )
  693. end
  694. defp restrict_hashtag_any(query, %{tag: tag}) when is_binary(tag) do
  695. restrict_hashtag_any(query, %{tag: [tag]})
  696. end
  697. defp restrict_hashtag_any(query, _), do: query
  698. defp restrict_hashtag_reject_any(_query, %{tag_reject: _tag_reject, skip_preload: true}) do
  699. raise_on_missing_preload()
  700. end
  701. defp restrict_hashtag_reject_any(query, %{tag_reject: [_ | _] = tags_reject}) do
  702. from(
  703. [_activity, object] in query,
  704. where: object.id not in subquery(object_ids_query_for_tags(tags_reject))
  705. )
  706. end
  707. defp restrict_hashtag_reject_any(query, %{tag_reject: tag_reject}) when is_binary(tag_reject) do
  708. restrict_hashtag_reject_any(query, %{tag_reject: [tag_reject]})
  709. end
  710. defp restrict_hashtag_reject_any(query, _), do: query
  711. defp raise_on_missing_preload do
  712. raise "Can't use the child object without preloading!"
  713. end
  714. defp restrict_recipients(query, [], _user), do: query
  715. defp restrict_recipients(query, recipients, nil) do
  716. from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
  717. end
  718. defp restrict_recipients(query, recipients, user) do
  719. from(
  720. activity in query,
  721. where: fragment("? && ?", ^recipients, activity.recipients),
  722. or_where: activity.actor == ^user.ap_id
  723. )
  724. end
  725. defp restrict_local(query, %{local_only: true}) do
  726. from(activity in query, where: activity.local == true)
  727. end
  728. defp restrict_local(query, _), do: query
  729. defp restrict_remote(query, %{remote: true}) do
  730. from(activity in query, where: activity.local == false)
  731. end
  732. defp restrict_remote(query, _), do: query
  733. defp restrict_actor(query, %{actor_id: actor_id}) do
  734. from(activity in query, where: activity.actor == ^actor_id)
  735. end
  736. defp restrict_actor(query, _), do: query
  737. defp restrict_type(query, %{type: type}) when is_binary(type) do
  738. from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
  739. end
  740. defp restrict_type(query, %{type: type}) do
  741. from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
  742. end
  743. defp restrict_type(query, _), do: query
  744. defp restrict_state(query, %{state: state}) do
  745. from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
  746. end
  747. defp restrict_state(query, _), do: query
  748. defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
  749. from(
  750. [_activity, object] in query,
  751. where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
  752. )
  753. end
  754. defp restrict_favorited_by(query, _), do: query
  755. defp restrict_media(_query, %{only_media: _val, skip_preload: true}) do
  756. raise "Can't use the child object without preloading!"
  757. end
  758. defp restrict_media(query, %{only_media: true}) do
  759. from(
  760. [activity, object] in query,
  761. where: fragment("(?)->>'type' = ?", activity.data, "Create"),
  762. where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
  763. )
  764. end
  765. defp restrict_media(query, _), do: query
  766. defp restrict_replies(query, %{exclude_replies: true}) do
  767. from(
  768. [_activity, object] in query,
  769. where: fragment("?->>'inReplyTo' is null", object.data)
  770. )
  771. end
  772. defp restrict_replies(query, %{
  773. reply_filtering_user: %User{} = user,
  774. reply_visibility: "self"
  775. }) do
  776. from(
  777. [activity, object] in query,
  778. where:
  779. fragment(
  780. "?->>'inReplyTo' is null OR ? = ANY(?)",
  781. object.data,
  782. ^user.ap_id,
  783. activity.recipients
  784. )
  785. )
  786. end
  787. defp restrict_replies(query, %{
  788. reply_filtering_user: %User{} = user,
  789. reply_visibility: "following"
  790. }) do
  791. from(
  792. [activity, object] in query,
  793. where:
  794. fragment(
  795. """
  796. ?->>'type' != 'Create' -- This isn't a Create
  797. OR ?->>'inReplyTo' is null -- this isn't a reply
  798. OR ? && array_remove(?, ?) -- The recipient is us or one of our friends,
  799. -- unless they are the author (because authors
  800. -- are also part of the recipients). This leads
  801. -- to a bug that self-replies by friends won't
  802. -- show up.
  803. OR ? = ? -- The actor is us
  804. """,
  805. activity.data,
  806. object.data,
  807. ^[user.ap_id | User.get_cached_user_friends_ap_ids(user)],
  808. activity.recipients,
  809. activity.actor,
  810. activity.actor,
  811. ^user.ap_id
  812. )
  813. )
  814. end
  815. defp restrict_replies(query, _), do: query
  816. defp restrict_reblogs(query, %{exclude_reblogs: true}) do
  817. from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
  818. end
  819. defp restrict_reblogs(query, _), do: query
  820. defp restrict_muted(query, %{with_muted: true}), do: query
  821. defp restrict_muted(query, %{muting_user: %User{} = user} = opts) do
  822. mutes = opts[:muted_users_ap_ids] || User.muted_users_ap_ids(user)
  823. query =
  824. from([activity] in query,
  825. where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
  826. where:
  827. fragment(
  828. "not (?->'to' \\?| ?) or ? = ?",
  829. activity.data,
  830. ^mutes,
  831. activity.actor,
  832. ^user.ap_id
  833. )
  834. )
  835. unless opts[:skip_preload] do
  836. from([thread_mute: tm] in query, where: is_nil(tm.user_id))
  837. else
  838. query
  839. end
  840. end
  841. defp restrict_muted(query, _), do: query
  842. defp restrict_blocked(query, %{blocking_user: %User{} = user} = opts) do
  843. blocked_ap_ids = opts[:blocked_users_ap_ids] || User.blocked_users_ap_ids(user)
  844. domain_blocks = user.domain_blocks || []
  845. following_ap_ids = User.get_friends_ap_ids(user)
  846. query =
  847. if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
  848. from(
  849. [activity, object: o] in query,
  850. where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
  851. where:
  852. fragment(
  853. "((not (? && ?)) or ? = ?)",
  854. activity.recipients,
  855. ^blocked_ap_ids,
  856. activity.actor,
  857. ^user.ap_id
  858. ),
  859. where:
  860. fragment(
  861. "recipients_contain_blocked_domains(?, ?) = false",
  862. activity.recipients,
  863. ^domain_blocks
  864. ),
  865. where:
  866. fragment(
  867. "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
  868. activity.data,
  869. activity.data,
  870. ^blocked_ap_ids
  871. ),
  872. where:
  873. fragment(
  874. "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
  875. activity.actor,
  876. ^domain_blocks,
  877. activity.actor,
  878. ^following_ap_ids
  879. ),
  880. where:
  881. fragment(
  882. "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
  883. o.data,
  884. ^domain_blocks,
  885. o.data,
  886. ^following_ap_ids
  887. )
  888. )
  889. end
  890. defp restrict_blocked(query, _), do: query
  891. defp restrict_unlisted(query, %{restrict_unlisted: true}) do
  892. from(
  893. activity in query,
  894. where:
  895. fragment(
  896. "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
  897. activity.data,
  898. ^[Constants.as_public()]
  899. )
  900. )
  901. end
  902. defp restrict_unlisted(query, _), do: query
  903. defp restrict_pinned(query, %{pinned: true, pinned_object_ids: ids}) do
  904. from(
  905. [activity, object: o] in query,
  906. where:
  907. fragment(
  908. "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)",
  909. activity.data,
  910. activity.data,
  911. activity.data,
  912. ^ids
  913. )
  914. )
  915. end
  916. defp restrict_pinned(query, _), do: query
  917. defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do
  918. muted_reblogs = opts[:reblog_muted_users_ap_ids] || User.reblog_muted_users_ap_ids(user)
  919. from(
  920. activity in query,
  921. where:
  922. fragment(
  923. "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
  924. activity.data,
  925. activity.actor,
  926. ^muted_reblogs
  927. )
  928. )
  929. end
  930. defp restrict_muted_reblogs(query, _), do: query
  931. defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
  932. from(
  933. activity in query,
  934. where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
  935. )
  936. end
  937. defp restrict_instance(query, _), do: query
  938. defp restrict_filtered(query, %{user: %User{} = user}) do
  939. case Filter.compose_regex(user) do
  940. nil ->
  941. query
  942. regex ->
  943. from([activity, object] in query,
  944. where:
  945. fragment("not(?->>'content' ~* ?)", object.data, ^regex) or
  946. activity.actor == ^user.ap_id
  947. )
  948. end
  949. end
  950. defp restrict_filtered(query, %{blocking_user: %User{} = user}) do
  951. restrict_filtered(query, %{user: user})
  952. end
  953. defp restrict_filtered(query, _), do: query
  954. defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
  955. defp exclude_poll_votes(query, _) do
  956. if has_named_binding?(query, :object) do
  957. from([activity, object: o] in query,
  958. where: fragment("not(?->>'type' = ?)", o.data, "Answer")
  959. )
  960. else
  961. query
  962. end
  963. end
  964. defp exclude_chat_messages(query, %{include_chat_messages: true}), do: query
  965. defp exclude_chat_messages(query, _) do
  966. if has_named_binding?(query, :object) do
  967. from([activity, object: o] in query,
  968. where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
  969. )
  970. else
  971. query
  972. end
  973. end
  974. defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query
  975. defp exclude_invisible_actors(query, _opts) do
  976. invisible_ap_ids =
  977. User.Query.build(%{invisible: true, select: [:ap_id]})
  978. |> Repo.all()
  979. |> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
  980. from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
  981. end
  982. defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do
  983. from(activity in query, where: activity.id != ^id)
  984. end
  985. defp exclude_id(query, _), do: query
  986. defp maybe_preload_objects(query, %{skip_preload: true}), do: query
  987. defp maybe_preload_objects(query, _) do
  988. query
  989. |> Activity.with_preloaded_object()
  990. end
  991. defp maybe_preload_bookmarks(query, %{skip_preload: true}), do: query
  992. defp maybe_preload_bookmarks(query, opts) do
  993. query
  994. |> Activity.with_preloaded_bookmark(opts[:user])
  995. end
  996. defp maybe_preload_report_notes(query, %{preload_report_notes: true}) do
  997. query
  998. |> Activity.with_preloaded_report_notes()
  999. end
  1000. defp maybe_preload_report_notes(query, _), do: query
  1001. defp maybe_set_thread_muted_field(query, %{skip_preload: true}), do: query
  1002. defp maybe_set_thread_muted_field(query, opts) do
  1003. query
  1004. |> Activity.with_set_thread_muted_field(opts[:muting_user] || opts[:user])
  1005. end
  1006. defp maybe_order(query, %{order: :desc}) do
  1007. query
  1008. |> order_by(desc: :id)
  1009. end
  1010. defp maybe_order(query, %{order: :asc}) do
  1011. query
  1012. |> order_by(asc: :id)
  1013. end
  1014. defp maybe_order(query, _), do: query
  1015. defp normalize_fetch_activities_query_opts(opts) do
  1016. Enum.reduce([:tag, :tag_all, :tag_reject], opts, fn key, opts ->
  1017. case opts[key] do
  1018. value when is_bitstring(value) ->
  1019. Map.put(opts, key, Hashtag.normalize_name(value))
  1020. value when is_list(value) ->
  1021. normalized_value =
  1022. value
  1023. |> Enum.map(&Hashtag.normalize_name/1)
  1024. |> Enum.uniq()
  1025. Map.put(opts, key, normalized_value)
  1026. _ ->
  1027. opts
  1028. end
  1029. end)
  1030. end
  1031. defp fetch_activities_query_ap_ids_ops(opts) do
  1032. source_user = opts[:muting_user]
  1033. ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: []
  1034. ap_id_relationships =
  1035. if opts[:blocking_user] && opts[:blocking_user] == source_user do
  1036. [:block | ap_id_relationships]
  1037. else
  1038. ap_id_relationships
  1039. end
  1040. preloaded_ap_ids = User.outgoing_relationships_ap_ids(source_user, ap_id_relationships)
  1041. restrict_blocked_opts = Map.merge(%{blocked_users_ap_ids: preloaded_ap_ids[:block]}, opts)
  1042. restrict_muted_opts = Map.merge(%{muted_users_ap_ids: preloaded_ap_ids[:mute]}, opts)
  1043. restrict_muted_reblogs_opts =
  1044. Map.merge(%{reblog_muted_users_ap_ids: preloaded_ap_ids[:reblog_mute]}, opts)
  1045. {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
  1046. end
  1047. def fetch_activities_query(recipients, opts \\ %{}) do
  1048. opts = normalize_fetch_activities_query_opts(opts)
  1049. {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
  1050. fetch_activities_query_ap_ids_ops(opts)
  1051. config = %{
  1052. skip_thread_containment: Config.get([:instance, :skip_thread_containment])
  1053. }
  1054. query =
  1055. Activity
  1056. |> maybe_preload_objects(opts)
  1057. |> maybe_preload_bookmarks(opts)
  1058. |> maybe_preload_report_notes(opts)
  1059. |> maybe_set_thread_muted_field(opts)
  1060. |> maybe_order(opts)
  1061. |> restrict_recipients(recipients, opts[:user])
  1062. |> restrict_replies(opts)
  1063. |> restrict_since(opts)
  1064. |> restrict_local(opts)
  1065. |> restrict_remote(opts)
  1066. |> restrict_actor(opts)
  1067. |> restrict_type(opts)
  1068. |> restrict_state(opts)
  1069. |> restrict_favorited_by(opts)
  1070. |> restrict_blocked(restrict_blocked_opts)
  1071. |> restrict_muted(restrict_muted_opts)
  1072. |> restrict_filtered(opts)
  1073. |> restrict_media(opts)
  1074. |> restrict_visibility(opts)
  1075. |> restrict_thread_visibility(opts, config)
  1076. |> restrict_reblogs(opts)
  1077. |> restrict_pinned(opts)
  1078. |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
  1079. |> restrict_instance(opts)
  1080. |> restrict_announce_object_actor(opts)
  1081. |> restrict_filtered(opts)
  1082. |> Activity.restrict_deactivated_users()
  1083. |> exclude_poll_votes(opts)
  1084. |> exclude_chat_messages(opts)
  1085. |> exclude_invisible_actors(opts)
  1086. |> exclude_visibility(opts)
  1087. if Config.feature_enabled?(:improved_hashtag_timeline) do
  1088. query
  1089. |> restrict_hashtag_any(opts)
  1090. |> restrict_hashtag_all(opts)
  1091. |> restrict_hashtag_reject_any(opts)
  1092. else
  1093. query
  1094. |> restrict_embedded_tag_any(opts)
  1095. |> restrict_embedded_tag_all(opts)
  1096. |> restrict_embedded_tag_reject_any(opts)
  1097. end
  1098. end
  1099. @doc """
  1100. Fetch favorites activities of user with order by sort adds to favorites
  1101. """
  1102. @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
  1103. def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
  1104. user.ap_id
  1105. |> Activity.Queries.by_actor()
  1106. |> Activity.Queries.by_type("Like")
  1107. |> Activity.with_joined_object()
  1108. |> Object.with_joined_activity()
  1109. |> select([like, object, activity], %{activity | object: object, pagination_id: like.id})
  1110. |> order_by([like, _, _], desc_nulls_last: like.id)
  1111. |> Pagination.fetch_paginated(
  1112. Map.merge(params, %{skip_order: true}),
  1113. pagination
  1114. )
  1115. end
  1116. defp maybe_update_cc(activities, [_ | _] = list_memberships, %User{ap_id: user_ap_id}) do
  1117. Enum.map(activities, fn
  1118. %{data: %{"bcc" => [_ | _] = bcc}} = activity ->
  1119. if Enum.any?(bcc, &(&1 in list_memberships)) do
  1120. update_in(activity.data["cc"], &[user_ap_id | &1])
  1121. else
  1122. activity
  1123. end
  1124. activity ->
  1125. activity
  1126. end)
  1127. end
  1128. defp maybe_update_cc(activities, _, _), do: activities
  1129. defp fetch_activities_bounded_query(query, recipients, recipients_with_public) do
  1130. from(activity in query,
  1131. where:
  1132. fragment("? && ?", activity.recipients, ^recipients) or
  1133. (fragment("? && ?", activity.recipients, ^recipients_with_public) and
  1134. ^Constants.as_public() in activity.recipients)
  1135. )
  1136. end
  1137. def fetch_activities_bounded(
  1138. recipients,
  1139. recipients_with_public,
  1140. opts \\ %{},
  1141. pagination \\ :keyset
  1142. ) do
  1143. fetch_activities_query([], opts)
  1144. |> fetch_activities_bounded_query(recipients, recipients_with_public)
  1145. |> Pagination.fetch_paginated(opts, pagination)
  1146. |> Enum.reverse()
  1147. end
  1148. @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
  1149. def upload(file, opts \\ []) do
  1150. with {:ok, data} <- Upload.store(file, opts) do
  1151. obj_data = Maps.put_if_present(data, "actor", opts[:actor])
  1152. Repo.insert(%Object{data: obj_data})
  1153. end
  1154. end
  1155. @spec get_actor_url(any()) :: binary() | nil
  1156. defp get_actor_url(url) when is_binary(url), do: url
  1157. defp get_actor_url(%{"href" => href}) when is_binary(href), do: href
  1158. defp get_actor_url(url) when is_list(url) do
  1159. url
  1160. |> List.first()
  1161. |> get_actor_url()
  1162. end
  1163. defp get_actor_url(_url), do: nil
  1164. defp normalize_image(%{"url" => url}) do
  1165. %{
  1166. "type" => "Image",
  1167. "url" => [%{"href" => url}]
  1168. }
  1169. end
  1170. defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
  1171. defp normalize_image(_), do: nil
  1172. defp object_to_user_data(data) do
  1173. fields =
  1174. data
  1175. |> Map.get("attachment", [])
  1176. |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
  1177. |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
  1178. emojis =
  1179. data
  1180. |> Map.get("tag", [])
  1181. |> Enum.filter(fn
  1182. %{"type" => "Emoji"} -> true
  1183. _ -> false
  1184. end)
  1185. |> Map.new(fn %{"icon" => %{"url" => url}, "name" => name} ->
  1186. {String.trim(name, ":"), url}
  1187. end)
  1188. is_locked = data["manuallyApprovesFollowers"] || false
  1189. capabilities = data["capabilities"] || %{}
  1190. accepts_chat_messages = capabilities["acceptsChatMessages"]
  1191. data = Transmogrifier.maybe_fix_user_object(data)
  1192. is_discoverable = data["discoverable"] || false
  1193. invisible = data["invisible"] || false
  1194. actor_type = data["type"] || "Person"
  1195. featured_address = data["featured"]
  1196. {:ok, pinned_objects} = fetch_and_prepare_featured_from_ap_id(featured_address)
  1197. public_key =
  1198. if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
  1199. data["publicKey"]["publicKeyPem"]
  1200. else
  1201. nil
  1202. end
  1203. shared_inbox =
  1204. if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
  1205. data["endpoints"]["sharedInbox"]
  1206. else
  1207. nil
  1208. end
  1209. user_data = %{
  1210. ap_id: data["id"],
  1211. uri: get_actor_url(data["url"]),
  1212. ap_enabled: true,
  1213. banner: normalize_image(data["image"]),
  1214. fields: fields,
  1215. emoji: emojis,
  1216. is_locked: is_locked,
  1217. is_discoverable: is_discoverable,
  1218. invisible: invisible,
  1219. avatar: normalize_image(data["icon"]),
  1220. name: data["name"],
  1221. follower_address: data["followers"],
  1222. following_address: data["following"],
  1223. featured_address: featured_address,
  1224. bio: data["summary"] || "",
  1225. actor_type: actor_type,
  1226. also_known_as: Map.get(data, "alsoKnownAs", []),
  1227. public_key: public_key,
  1228. inbox: data["inbox"],
  1229. shared_inbox: shared_inbox,
  1230. accepts_chat_messages: accepts_chat_messages,
  1231. pinned_objects: pinned_objects
  1232. }
  1233. # nickname can be nil because of virtual actors
  1234. if data["preferredUsername"] do
  1235. Map.put(
  1236. user_data,
  1237. :nickname,
  1238. "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
  1239. )
  1240. else
  1241. Map.put(user_data, :nickname, nil)
  1242. end
  1243. end
  1244. def fetch_follow_information_for_user(user) do
  1245. with {:ok, following_data} <-
  1246. Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
  1247. {:ok, hide_follows} <- collection_private(following_data),
  1248. {:ok, followers_data} <-
  1249. Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
  1250. {:ok, hide_followers} <- collection_private(followers_data) do
  1251. {:ok,
  1252. %{
  1253. hide_follows: hide_follows,
  1254. follower_count: normalize_counter(followers_data["totalItems"]),
  1255. following_count: normalize_counter(following_data["totalItems"]),
  1256. hide_followers: hide_followers
  1257. }}
  1258. else
  1259. {:error, _} = e -> e
  1260. e -> {:error, e}
  1261. end
  1262. end
  1263. defp normalize_counter(counter) when is_integer(counter), do: counter
  1264. defp normalize_counter(_), do: 0
  1265. def maybe_update_follow_information(user_data) do
  1266. with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
  1267. {_, true} <- {:user_type_check, user_data[:type] in ["Person", "Service"]},
  1268. {_, true} <-
  1269. {:collections_available,
  1270. !!(user_data[:following_address] && user_data[:follower_address])},
  1271. {:ok, info} <-
  1272. fetch_follow_information_for_user(user_data) do
  1273. info = Map.merge(user_data[:info] || %{}, info)
  1274. user_data
  1275. |> Map.put(:info, info)
  1276. else
  1277. {:user_type_check, false} ->
  1278. user_data
  1279. {:collections_available, false} ->
  1280. user_data
  1281. {:enabled, false} ->
  1282. user_data
  1283. e ->
  1284. Logger.error(
  1285. "Follower/Following counter update for #{user_data.ap_id} failed.\n" <> inspect(e)
  1286. )
  1287. user_data
  1288. end
  1289. end
  1290. defp collection_private(%{"first" => %{"type" => type}})
  1291. when type in ["CollectionPage", "OrderedCollectionPage"],
  1292. do: {:ok, false}
  1293. defp collection_private(%{"first" => first}) do
  1294. with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
  1295. Fetcher.fetch_and_contain_remote_object_from_id(first) do
  1296. {:ok, false}
  1297. else
  1298. {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
  1299. {:error, _} = e -> e
  1300. e -> {:error, e}
  1301. end
  1302. end
  1303. defp collection_private(_data), do: {:ok, true}
  1304. def user_data_from_user_object(data) do
  1305. with {:ok, data} <- MRF.filter(data) do
  1306. {:ok, object_to_user_data(data)}
  1307. else
  1308. e -> {:error, e}
  1309. end
  1310. end
  1311. def fetch_and_prepare_user_from_ap_id(ap_id) do
  1312. with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
  1313. {:ok, data} <- user_data_from_user_object(data) do
  1314. {:ok, maybe_update_follow_information(data)}
  1315. else
  1316. # If this has been deleted, only log a debug and not an error
  1317. {:error, "Object has been deleted" = e} ->
  1318. Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
  1319. {:error, e}
  1320. {:error, {:reject, reason} = e} ->
  1321. Logger.info("Rejected user #{ap_id}: #{inspect(reason)}")
  1322. {:error, e}
  1323. {:error, e} ->
  1324. Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
  1325. {:error, e}
  1326. end
  1327. end
  1328. def maybe_handle_clashing_nickname(data) do
  1329. with nickname when is_binary(nickname) <- data[:nickname],
  1330. %User{} = old_user <- User.get_by_nickname(nickname),
  1331. {_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do
  1332. Logger.info(
  1333. "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{
  1334. data[:ap_id]
  1335. }, renaming."
  1336. )
  1337. old_user
  1338. |> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
  1339. |> User.update_and_set_cache()
  1340. else
  1341. {:ap_id_comparison, true} ->
  1342. Logger.info(
  1343. "Found an old user for #{data[:nickname]}, but the ap id #{data[:ap_id]} is the same as the new user. Race condition? Not changing anything."
  1344. )
  1345. _ ->
  1346. nil
  1347. end
  1348. end
  1349. def pin_data_from_featured_collection(%{
  1350. "type" => type,
  1351. "orderedItems" => objects
  1352. })
  1353. when type in ["OrderedCollection", "Collection"] do
  1354. Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end)
  1355. end
  1356. def fetch_and_prepare_featured_from_ap_id(nil) do
  1357. {:ok, %{}}
  1358. end
  1359. def fetch_and_prepare_featured_from_ap_id(ap_id) do
  1360. with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
  1361. {:ok, pin_data_from_featured_collection(data)}
  1362. else
  1363. e ->
  1364. Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}")
  1365. {:ok, %{}}
  1366. end
  1367. end
  1368. def pinned_fetch_task(nil), do: nil
  1369. def pinned_fetch_task(%{pinned_objects: pins}) do
  1370. if Enum.all?(pins, fn {ap_id, _} ->
  1371. Object.get_cached_by_ap_id(ap_id) ||
  1372. match?({:ok, _object}, Fetcher.fetch_object_from_id(ap_id))
  1373. end) do
  1374. :ok
  1375. else
  1376. :error
  1377. end
  1378. end
  1379. def make_user_from_ap_id(ap_id) do
  1380. user = User.get_cached_by_ap_id(ap_id)
  1381. if user && !User.ap_enabled?(user) do
  1382. Transmogrifier.upgrade_user_from_ap_id(ap_id)
  1383. else
  1384. with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
  1385. {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
  1386. if user do
  1387. user
  1388. |> User.remote_user_changeset(data)
  1389. |> User.update_and_set_cache()
  1390. else
  1391. maybe_handle_clashing_nickname(data)
  1392. data
  1393. |> User.remote_user_changeset()
  1394. |> Repo.insert()
  1395. |> User.set_cache()
  1396. end
  1397. end
  1398. end
  1399. end
  1400. def make_user_from_nickname(nickname) do
  1401. with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
  1402. make_user_from_ap_id(ap_id)
  1403. else
  1404. _e -> {:error, "No AP id in WebFinger"}
  1405. end
  1406. end
  1407. # filter out broken threads
  1408. defp contain_broken_threads(%Activity{} = activity, %User{} = user) do
  1409. entire_thread_visible_for_user?(activity, user)
  1410. end
  1411. # do post-processing on a specific activity
  1412. def contain_activity(%Activity{} = activity, %User{} = user) do
  1413. contain_broken_threads(activity, user)
  1414. end
  1415. def fetch_direct_messages_query do
  1416. Activity
  1417. |> restrict_type(%{type: "Create"})
  1418. |> restrict_visibility(%{visibility: "direct"})
  1419. |> order_by([activity], asc: activity.id)
  1420. end
  1421. end