1 # Fedi API Block - An aggregator for fetching blocking data from fediverse nodes
2 # Copyright (C) 2023 Free Software Foundation
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published
6 # by the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <https://www.gnu.org/licenses/>.
20 from fba import blacklist
21 from fba import blocks
23 from fba import federation
24 from fba import instances
25 from fba.helpers import tidyup
27 def fetch_blocks(domain: str, origin: str, nodeinfo_url: str):
28 # DEBUG: print(f"DEBUG: domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}' - CALLED!")
29 if not isinstance(domain, str):
30 raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
32 raise ValueError("Parameter 'domain' is empty")
33 elif not isinstance(origin, str) and origin is not None:
34 raise ValueError(f"Parameter origin[]={type(origin)} is not 'str'")
36 raise ValueError("Parameter 'origin' is empty")
37 elif not isinstance(nodeinfo_url, str):
38 raise ValueError(f"Parameter nodeinfo_url[]={type(nodeinfo_url)} is not 'str'")
39 elif nodeinfo_url == "":
40 raise ValueError("Parameter 'nodeinfo_url' is empty")
45 rows = federation.fetch_nodeinfo(domain, nodeinfo_url)
48 print("WARNING: Could not fetch nodeinfo from domain:", domain)
50 elif "metadata" not in rows:
51 print(f"WARNING: rows()={len(rows)} does not have key 'metadata', domain='{domain}'")
53 elif "federation" not in rows["metadata"]:
54 print(f"WARNING: rows()={len(rows['metadata'])} does not have key 'federation', domain='{domain}'")
57 # DEBUG: print("DEBUG: Updating nodeinfo:", domain)
58 instances.update_last_nodeinfo(domain)
60 data = rows["metadata"]["federation"]
63 # DEBUG: print("DEBUG: Instance has no block list to analyze:", domain)
66 if "mrf_simple" in data:
67 for block_level, blocklist in (
68 {**data["mrf_simple"],
69 **{"quarantined_instances": data["quarantined_instances"]}}
71 # DEBUG: print("DEBUG: block_level, blocklist():", block_level, len(blocklist))
72 block_level = tidyup.domain(block_level)
73 # DEBUG: print("DEBUG: BEFORE block_level:", block_level)
76 print("WARNING: block_level is now empty!")
79 # DEBUG: print(f"DEBUG: Checking {len(blocklist)} entries from domain='{domain}',block_level='{block_level}' ...")
80 for blocked in blocklist:
81 # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
82 blocked = tidyup.domain(blocked)
83 # DEBUG: print("DEBUG: AFTER blocked:", blocked)
86 print("WARNING: blocked is empty after tidyup.domain():", domain, block_level)
88 elif blacklist.is_blacklisted(blocked):
89 # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
91 elif blocked.count("*") > 1:
92 # -ACK!-oma also started obscuring domains without hash
94 "SELECT domain, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
96 searchres = fba.cursor.fetchone()
98 print(f"DEBUG: searchres[]='{type(searchres)}'")
100 print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
103 blocked = searchres[0]
104 nodeinfo_url = searchres[1]
105 # DEBUG: print("DEBUG: Looked up domain:", blocked)
106 elif not validators.domain(blocked):
107 print(f"WARNING: blocked='{blocked}',software='pleroma' is not a valid domain name - skipped!")
110 # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
111 if not validators.domain(blocked):
112 print(f"WARNING: blocked='{blocked}',software='pleroma' is not a valid domain name - skipped!")
114 elif not instances.is_registered(blocked):
115 # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
116 instances.add(blocked, domain, inspect.currentframe().f_code.co_name, nodeinfo_url)
118 if not blocks.is_instance_blocked(domain, blocked, block_level):
119 # DEBUG: print("DEBUG: Blocking:", domain, blocked, block_level)
120 blocks.add_instance(domain, blocked, "unknown", block_level)
122 if block_level == "reject":
123 # DEBUG: print("DEBUG: Adding to blockdict:", blocked)
130 # DEBUG: print(f"DEBUG: Updating block last seen for domain='{domain}',blocked='{blocked}' ...")
131 blocks.update_last_seen(domain, blocked, block_level)
133 # DEBUG: print("DEBUG: Committing changes ...")
134 fba.connection.commit()
137 if "mrf_simple_info" in data:
138 # DEBUG: print("DEBUG: Found mrf_simple_info:", domain)
139 for block_level, info in (
140 {**data["mrf_simple_info"],
141 **(data["quarantined_instances_info"]
142 if "quarantined_instances_info" in data
145 # DEBUG: print("DEBUG: block_level, info.items():", block_level, len(info.items()))
146 block_level = tidyup.domain(block_level)
147 # DEBUG: print("DEBUG: BEFORE block_level:", block_level)
149 if block_level == "":
150 print("WARNING: block_level is now empty!")
153 # DEBUG: print(f"DEBUG: Checking {len(info.items())} entries from domain='{domain}',software='pleroma',block_level='{block_level}' ...")
154 for blocked, reason in info.items():
155 # DEBUG: print(f"DEBUG: blocked='{blocked}',reason='{reason}' - BEFORE!")
156 blocked = tidyup.domain(blocked)
157 reason = tidyup.reason(reason) if reason is not None and reason != "" else None
158 # DEBUG: print(f"DEBUG: blocked='{blocked}',reason='{reason}' - AFTER!")
161 print("WARNING: blocked is empty after tidyup.domain():", domain, block_level)
163 elif not validators.domain(blocked):
164 print(f"WARNING: blocked='{blocked}',software='pleroma' is not a valid domain name - skipped!")
166 elif blacklist.is_blacklisted(blocked):
167 # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
169 elif blocked.count("*") > 1:
170 # same domain guess as above, but for reasons field
172 "SELECT domain, origin, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
174 searchres = fba.cursor.fetchone()
176 print(f"DEBUG: searchres[]='{type(searchres)}'")
177 if searchres is None:
178 print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
181 blocked = searchres[0]
182 origin = searchres[1]
183 nodeinfo_url = searchres[2]
185 # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
186 if not instances.is_registered(blocked):
187 # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
188 instances.add(blocked, domain, inspect.currentframe().f_code.co_name, nodeinfo_url)
190 # DEBUG: print("DEBUG: Updating block reason:", domain, blocked, reason["reason"])
191 blocks.update_reason(reason["reason"], domain, blocked, block_level)
193 # DEBUG: print(f"DEBUG: blockdict()={len(blockdict)}")
194 for entry in blockdict:
195 if entry["blocked"] == blocked:
196 # DEBUG: print("DEBUG: Updating entry reason:", blocked)
197 entry["reason"] = reason["reason"]
199 fba.connection.commit()
200 except BaseException as exception:
201 print(f"ERROR: domain='{domain}',software='pleroma',exception[{type(exception)}]:'{str(exception)}'")
203 # DEBUG: print("DEBUG: EXIT!")