-
Notifications
You must be signed in to change notification settings - Fork 9
/
fetcher.ex
121 lines (103 loc) · 3.09 KB
/
fetcher.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
defmodule ActivityPub.Fetcher do
@moduledoc """
Handles fetching AS2 objects from remote instances.
"""
alias ActivityPub.HTTP
alias ActivityPub.Object
alias ActivityPubWeb.Transmogrifier
require Logger
@create_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]
@doc """
Checks if an object exists in the database and fetches it if it doesn't.
"""
def fetch_object_from_id(id) do
if object = Object.get_cached_by_ap_id(id) do
{:ok, object}
else
with {:ok, data} <- fetch_remote_object_from_id(id),
{:ok, data} <- contain_origin(data),
{:ok, object} <- insert_object(data),
{:ok} <- check_if_public(object.public) do
{:ok, object}
else
{:error, e} ->
{:error, e}
end
end
end
@doc """
Fetches an AS2 object from remote AP ID.
"""
def fetch_remote_object_from_id(id) do
Logger.info("Fetching object #{id} via AP")
with true <- String.starts_with?(id, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <-
HTTP.get(
id,
[{:Accept, "application/activity+json"}]
),
{:ok, data} <- Jason.decode(body),
{:ok, data} <- contain_uri(id, data) do
{:ok, data}
else
{:ok, %{status: code}} when code in [404, 410] ->
{:error, "Object has been deleted"}
{:error, %Jason.DecodeError{} = _error} ->
{:error, "Invalid AP JSON"}
{:error, e} ->
{:error, e}
e ->
{:error, e}
end
end
@skipped_types [
"Person",
"Group",
"Collection",
"OrderedCollection",
"CollectionPage",
"OrderedCollectionPage"
]
defp contain_origin(%{"id" => id} = data) do
if data["type"] in @skipped_types do
{:ok, data}
else
actor = get_actor(data)
actor_uri = URI.parse(actor)
id_uri = URI.parse(id)
if id_uri.host == actor_uri.host do
{:ok, data}
else
{:error, "Object containment error"}
end
end
end
# Wrapping object in a create activity to easily pass it to the MN database.
defp insert_object(%{"type" => type} = data) when type in @create_object_types do
with params <- %{
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["actor"] || data["attributedTo"],
"object" => data
},
{:ok, activity} <- Transmogrifier.handle_incoming(params),
object <- activity.object do
{:ok, object}
end
end
defp insert_object(data), do: Transmogrifier.handle_object(data)
def get_actor(%{"attributedTo" => actor} = _data), do: actor
def get_actor(%{"actor" => actor} = _data), do: actor
defp check_if_public(public) when public == true, do: {:ok}
defp check_if_public(_public), do: {:error, "Not public"}
defp contain_uri(id, %{"id" => json_id} = data) do
id_uri = URI.parse(id)
json_id_uri = URI.parse(json_id)
if id_uri.host == json_id_uri.host do
{:ok, data}
else
{:error, "URI containment error"}
end
end
end