Gitlab: Run benchmark in CI.
[akkoma] / lib / pleroma / object / fetcher.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.Object.Fetcher do
6 alias Pleroma.HTTP
7 alias Pleroma.Object
8 alias Pleroma.Object.Containment
9 alias Pleroma.Signature
10 alias Pleroma.Web.ActivityPub.InternalFetchActor
11 alias Pleroma.Web.ActivityPub.Transmogrifier
12 alias Pleroma.Web.OStatus
13
14 require Logger
15
16 defp reinject_object(data) do
17 Logger.debug("Reinjecting object #{data["id"]}")
18
19 with data <- Transmogrifier.fix_object(data),
20 {:ok, object} <- Object.create(data) do
21 {:ok, object}
22 else
23 e ->
24 Logger.error("Error while processing object: #{inspect(e)}")
25 {:error, e}
26 end
27 end
28
29 # TODO:
30 # This will create a Create activity, which we need internally at the moment.
31 def fetch_object_from_id(id, options \\ []) do
32 if object = Object.get_cached_by_ap_id(id) do
33 {:ok, object}
34 else
35 Logger.info("Fetching #{id} via AP")
36
37 with {:fetch, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
38 {:normalize, nil} <- {:normalize, Object.normalize(data, false)},
39 params <- %{
40 "type" => "Create",
41 "to" => data["to"],
42 "cc" => data["cc"],
43 # Should we seriously keep this attributedTo thing?
44 "actor" => data["actor"] || data["attributedTo"],
45 "object" => data
46 },
47 {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
48 {:ok, activity} <- Transmogrifier.handle_incoming(params, options),
49 {:object, _data, %Object{} = object} <-
50 {:object, data, Object.normalize(activity, false)} do
51 {:ok, object}
52 else
53 {:containment, _} ->
54 {:error, "Object containment failed."}
55
56 {:error, {:reject, nil}} ->
57 {:reject, nil}
58
59 {:object, data, nil} ->
60 reinject_object(data)
61
62 {:normalize, object = %Object{}} ->
63 {:ok, object}
64
65 _e ->
66 # Only fallback when receiving a fetch/normalization error with ActivityPub
67 Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
68
69 # FIXME: OStatus Object Containment?
70 case OStatus.fetch_activity_from_url(id) do
71 {:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)}
72 e -> e
73 end
74 end
75 end
76 end
77
78 def fetch_object_from_id!(id, options \\ []) do
79 with {:ok, object} <- fetch_object_from_id(id, options) do
80 object
81 else
82 _e ->
83 nil
84 end
85 end
86
87 defp make_signature(id, date) do
88 uri = URI.parse(id)
89
90 signature =
91 InternalFetchActor.get_actor()
92 |> Signature.sign(%{
93 "(request-target)": "get #{uri.path}",
94 host: uri.host,
95 date: date
96 })
97
98 [{:Signature, signature}]
99 end
100
101 defp sign_fetch(headers, id, date) do
102 if Pleroma.Config.get([:activitypub, :sign_object_fetches]) do
103 headers ++ make_signature(id, date)
104 else
105 headers
106 end
107 end
108
109 defp maybe_date_fetch(headers, date) do
110 if Pleroma.Config.get([:activitypub, :sign_object_fetches]) do
111 headers ++ [{:Date, date}]
112 else
113 headers
114 end
115 end
116
117 def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
118 Logger.info("Fetching object #{id} via AP")
119
120 date = Pleroma.Signature.signed_date()
121
122 headers =
123 [{:Accept, "application/activity+json"}]
124 |> maybe_date_fetch(date)
125 |> sign_fetch(id, date)
126
127 Logger.debug("Fetch headers: #{inspect(headers)}")
128
129 with true <- String.starts_with?(id, "http"),
130 {:ok, %{body: body, status: code}} when code in 200..299 <- HTTP.get(id, headers),
131 {:ok, data} <- Jason.decode(body),
132 :ok <- Containment.contain_origin_from_id(id, data) do
133 {:ok, data}
134 else
135 {:ok, %{status: code}} when code in [404, 410] ->
136 {:error, "Object has been deleted"}
137
138 e ->
139 {:error, e}
140 end
141 end
142
143 def fetch_and_contain_remote_object_from_id(%{"id" => id}),
144 do: fetch_and_contain_remote_object_from_id(id)
145
146 def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"}
147 end