1 # Copyright (C) 2023 Free Software Foundation
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License as published
5 # by the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU Affero General Public License for more details.
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <https://www.gnu.org/licenses/>.
18 from urllib.parse import urlparse
24 from fba.helpers import config
25 from fba.helpers import cookies
26 from fba.helpers import domain as domain_helper
27 from fba.helpers import software as software_helper
28 from fba.helpers import tidyup
29 from fba.helpers import version
31 from fba.http import csrf
32 from fba.http import network
33 from fba.http import nodeinfo
35 from fba.models import blocks
36 from fba.models import instances
38 from fba.networks import lemmy
39 from fba.networks import misskey
40 from fba.networks import peertube
42 # Depth counter, being raised and lowered
45 logging.basicConfig(level=logging.INFO)
46 logger = logging.getLogger(__name__)
48 def fetch_instances(domain: str, origin: str, software: str, command: str, path: str = None):
50 logger.debug("domain='%s',origin='%s',software='%s',command='%s',path='%s',_DEPTH=%d - CALLED!", domain, origin, software, command, path, _DEPTH)
51 domain_helper.raise_on(domain)
53 if not isinstance(origin, str) and origin is not None:
54 raise ValueError(f"Parameter origin[]='{type(origin)}' is not of type 'str'")
55 elif not isinstance(command, str):
56 raise ValueError(f"Parameter command[]='{type(command)}' is not of type 'str'")
58 raise ValueError("Parameter 'command' is empty")
59 elif command in ["fetch_blocks", "fetch_cs", "fetch_bkali", "fetch_relays", "fetch_fedipact", "fetch_joinmobilizon", "fetch_joinmisskey", "fetch_joinfediverse", "fetch_relaylist"] and origin is None:
60 raise ValueError(f"Parameter command='{command}' but origin is None, please fix invoking this function.")
61 elif not isinstance(path, str) and path is not None:
62 raise ValueError(f"Parameter path[]='{type(path)}' is not of type 'str'")
63 elif _DEPTH > 0 and instances.is_recent(domain, "last_instance_fetch"):
64 raise ValueError(f"domain='{domain}' has recently been fetched but function was invoked")
65 elif software is None and not instances.is_recent(domain, "last_nodeinfo"):
67 logger.debug("Software for domain='%s' is not set, determining ...", domain)
68 software = determine_software(domain, path)
69 except network.exceptions as exception:
70 logger.warning("Exception '%s' during determining software type", type(exception))
71 instances.set_last_error(domain, exception)
73 logger.debug("Determined software='%s' for domain='%s'", software, domain)
74 elif software is None:
75 logger.debug("domain='%s' has unknown software or nodeinfo has recently being fetched", domain)
76 elif not isinstance(software, str):
77 raise ValueError(f"Parameter software[]='{type(software)}' is not of type 'str'")
82 logger.debug("Checking if domain='%s' is registered ...", domain)
83 if not instances.is_registered(domain):
84 logger.debug("Adding new domain='%s',origin='%s',command='%s',path='%s',software='%s'", domain, origin, command, path, software)
85 instances.add(domain, origin, command, path, software)
87 logger.debug("software='%s'", software)
88 if software_helper.is_relay(software):
89 logger.debug("software='%s' is a relay software - EXIT!", software)
93 logger.debug("Updating last_instance_fetch for domain='%s' ...", domain)
94 instances.set_last_instance_fetch(domain)
97 logger.debug("software='%s'", software)
98 if software is not None:
100 logger.debug("Fetching instances for domain='%s',software='%s',origin='%s'", domain, software, origin)
101 peerlist = fetch_peers(domain, software, origin)
102 except network.exceptions as exception:
103 logger.warning("Cannot fetch peers from domain='%s',software='%s': '%s'", domain, software, type(exception))
105 logger.debug("peerlist[]='%s'", type(peerlist))
106 if isinstance(peerlist, list):
107 logger.debug("Invoking instances.set_total_peerlist(%s,%d) ...", domain, len(peerlist))
108 instances.set_total_peers(domain, peerlist)
110 logger.debug("Invoking cookies.clear(%s) ...", domain)
111 cookies.clear(domain)
113 logger.debug("peerlist[]='%s'", type(peerlist))
115 logger.warning("Cannot fetch peers: domain='%s',software='%s'", domain, software)
117 if instances.has_pending(domain):
118 logger.debug("Flushing updates for domain='%s' ...", domain)
119 instances.update(domain)
122 logger.debug("EXIT!")
124 elif len(peerlist) == 0:
125 logger.info("domain='%s' returned an empty peer list.", domain)
127 if instances.has_pending(domain):
128 logger.debug("Flushing updates for domain='%s' ...", domain)
129 instances.update(domain)
132 logger.debug("domain='%s',software='%s' has an empty peer list returned - EXIT!", domain, software)
135 logger.info("Checking %d instance(s) from domain='%s',software='%s',depth=%d ...", len(peerlist), domain, software, _DEPTH)
136 for instance in peerlist:
137 logger.debug("instance='%s'", instance)
138 if instance is None or instance == "":
139 logger.debug("instance[%s]='%s' is either None or empty - SKIPPED!", type(instance), instance)
142 logger.debug("instance='%s' - BEFORE!", instance)
143 instance = tidyup.domain(instance) if isinstance(instance, str) and instance != "" else None
144 logger.debug("instance='%s' - AFTER!", instance)
146 if instance is None or instance == "":
147 logger.warning("instance='%s' is empty after tidyup.domain(), domain='%s'", instance, domain)
149 elif ".." in instance:
150 logger.warning("instance='%s' contains double-dot, removing ...", instance)
151 instance = instance.replace("..", ".")
153 logger.debug("instance='%s' - BEFORE!", instance)
154 instance = instance.encode("idna").decode("utf-8")
155 logger.debug("instance='%s' - AFTER!", instance)
157 if not domain_helper.is_wanted(instance):
158 logger.debug("instance='%s' is not wanted - SKIPPED!", instance)
160 elif instance.find("/profile/") > 0 or instance.find("/users/") > 0 or (instances.is_registered(instance.split("/")[0]) and instance.find("/c/") > 0):
161 logger.debug("instance='%s' is a link to a single user profile - SKIPPED!", instance)
163 elif instance.find("/tag/") > 0:
164 logger.debug("instance='%s' is a link to a tag - SKIPPED!", instance)
166 elif not instances.is_registered(instance):
167 logger.debug("Checking if domain='%s' has pending updates ...", domain)
168 if instances.has_pending(domain):
169 logger.debug("Flushing updates for domain='%s' ...", domain)
170 instances.update(domain)
172 logger.debug("instance='%s',origin='%s',_DEPTH=%d reached!", instance, origin, _DEPTH)
173 if _DEPTH <= config.get("max_crawl_depth") and len(peerlist) >= config.get("min_peers_length"):
174 logger.debug("Fetching instance='%s',origin='%s',command='%s',path='%s',_DEPTH=%d ...", instance, domain, command, path, _DEPTH)
175 fetch_instances(instance, domain, None, command, path)
177 logger.debug("Adding instance='%s',domain='%s',command='%s',_DEPTH=%d ...", instance, domain, command, _DEPTH)
178 instances.add(instance, domain, command)
180 logger.debug("Checking if domain='%s' has pending updates ...", domain)
181 if instances.has_pending(domain):
182 logger.debug("Flushing updates for domain='%s' ...", domain)
183 instances.update(domain)
186 logger.debug("EXIT!")
188 def fetch_peers(domain: str, software: str, origin: str) -> list:
189 logger.debug("domain='%s',software='%s',origin='%s' - CALLED!", domain, software, origin)
190 domain_helper.raise_on(domain)
192 if not isinstance(software, str) and software is not None:
193 raise ValueError(f"Parameter software[]='{type(software)}' is not of type 'str'")
194 elif isinstance(software, str) and software == "":
195 raise ValueError("Parameter 'software' is empty")
196 elif software_helper.is_relay(software):
197 raise ValueError(f"domain='{domain}' is of software='{software}' and isn't supported here.")
198 elif not isinstance(origin, str) and origin is not None:
199 raise ValueError(f"Parameter origin[]='{type(origin)}' is not of type 'str'")
200 elif isinstance(origin, str) and origin == "":
201 raise ValueError("Parameter 'origin' is empty")
203 if software == "misskey":
204 logger.debug("Invoking misskey.fetch_peers(%s) ...", domain)
205 return misskey.fetch_peers(domain)
206 elif software == "lemmy":
207 logger.debug("Invoking lemmy.fetch_peers(%s,%s) ...", domain, origin)
208 return lemmy.fetch_peers(domain, origin)
209 elif software == "peertube":
210 logger.debug("Invoking peertube.fetch_peers(%s) ...", domain)
211 return peertube.fetch_peers(domain)
213 # No CSRF by default, you don't have to add network.api_headers by yourself here
217 logger.debug("Checking CSRF for domain='%s'", domain)
218 headers = csrf.determine(domain, dict())
219 except network.exceptions as exception:
220 logger.warning("Exception '%s' during checking CSRF (fetch_peers,%s)", type(exception), __name__)
221 instances.set_last_error(domain, exception)
223 logger.debug("Returning empty list ... - EXIT!")
227 "/api/v1/instance/peers",
231 # Init peers variable
234 logger.debug("Checking %d paths ...", len(paths))
236 logger.debug("Fetching path='%s' from domain='%s',software='%s' ...", path, domain, software)
237 data = network.get_json_api(
241 timeout=(config.get("connection_timeout"), config.get("read_timeout"))
244 logger.debug("data[]='%s'", type(data))
245 if "error_message" in data:
246 logger.debug("Was not able to fetch peers from path='%s',domain='%s' ...", path, domain)
247 instances.set_last_error(domain, data)
248 elif "json" in data and len(data["json"]) > 0:
249 logger.debug("Querying API path='%s' was successful: domain='%s',data[json][%s]()=%d", path, domain, type(data['json']), len(data['json']))
252 logger.debug("Marking domain='%s' as successfully handled ...", domain)
253 instances.set_success(domain)
256 if not isinstance(peers, list):
257 logger.warning("peers[]='%s' is not of type 'list', maybe bad API response?", type(peers))
260 logger.debug("Invoking instances.set_total_peers(%s,%d) ...", domain, len(peers))
261 instances.set_total_peers(domain, peers)
263 logger.debug("peers()=%d - EXIT!", len(peers))
266 def fetch_generator_from_path(domain: str, path: str = "/") -> str:
267 logger.debug("domain='%s',path='%s' - CALLED!", domain, path)
268 domain_helper.raise_on(domain)
270 if not isinstance(path, str):
271 raise ValueError(f"path[]='{type(path)}' is not of type 'str'")
273 raise ValueError("Parameter 'path' is empty")
277 logger.debug("Fetching path='%s' from domain='%s' ...", path, domain)
278 response = network.fetch_response(
281 headers=network.web_headers,
282 timeout=(config.get("connection_timeout"), config.get("read_timeout")),
286 logger.debug("response.ok='%s',response.status_code=%d,response.text()=%d", response.ok, response.status_code, len(response.text))
287 if ((response.ok and response.status_code == 200) or response.status_code == 410) and response.text.find("<html") > 0 and domain_helper.is_in_url(domain, response.url):
288 logger.debug("Parsing response.text()=%d Bytes ...", len(response.text))
289 doc = bs4.BeautifulSoup(response.text, "html.parser")
291 logger.debug("doc[]='%s'", type(doc))
292 platform = doc.find("meta", {"property": "og:platform"})
293 generator = doc.find("meta", {"name" : "generator"})
294 site_name = doc.find("meta", {"property": "og:site_name"})
295 app_name = doc.find("meta", {"name" : "application-name"})
297 logger.debug("generator[]='%s',site_name[]='%s',platform[]='%s',app_name[]='%s'", type(generator), type(site_name), type(platform), type(app_name))
298 if isinstance(platform, bs4.element.Tag) and isinstance(platform.get("content"), str) and platform.get("content") != "":
299 logger.debug("Found property=og:platform, domain='%s'", domain)
300 software = tidyup.domain(platform.get("content"))
302 logger.debug("software[%s]='%s'", type(software), software)
303 if software is not None and software != "":
304 logger.debug("domain='%s' has og:platform='%s' - Setting detection_mode=PLATFORM ...", domain, software)
305 instances.set_detection_mode(domain, "PLATFORM")
306 elif isinstance(generator, bs4.element.Tag) and isinstance(generator.get("content"), str) and generator.get("content") != "":
307 logger.debug("Found generator meta tag: domain='%s'", domain)
308 software = tidyup.domain(generator.get("content"))
310 logger.debug("software[%s]='%s'", type(software), software)
311 if software is not None and software != "":
312 logger.info("domain='%s' is generated by software='%s' - Setting detection_mode=GENERATOR ...", domain, software)
313 instances.set_detection_mode(domain, "GENERATOR")
314 elif isinstance(app_name, bs4.element.Tag) and isinstance(app_name.get("content"), str) and app_name.get("content") != "":
315 logger.debug("Found property=og:app_name, domain='%s'", domain)
316 software = tidyup.domain(app_name.get("content"))
318 logger.debug("software[%s]='%s'", type(software), software)
319 if software is not None and software != "":
320 logger.debug("domain='%s' has application-name='%s' - Setting detection_mode=app_name ...", domain, software)
321 instances.set_detection_mode(domain, "APP_NAME")
322 elif isinstance(site_name, bs4.element.Tag) and isinstance(site_name.get("content"), str) and site_name.get("content") != "":
323 logger.debug("Found property=og:site_name, domain='%s'", domain)
324 software = tidyup.domain(site_name.get("content"))
326 logger.debug("software[%s]='%s'", type(software), software)
327 if software is not None and software != "":
328 logger.debug("domain='%s' has og:site_name='%s' - Setting detection_mode=SITE_NAME ...", domain, software)
329 instances.set_detection_mode(domain, "SITE_NAME")
330 elif not domain_helper.is_in_url(domain, response.url):
331 logger.warning("domain='%s' doesn't match response.url='%s', maybe redirect to other domain?", domain, response.url)
333 components = urlparse(response.url)
334 domain2 = components.netloc.lower().split(":")[0]
336 logger.debug("domain2='%s'", domain2)
337 if not domain_helper.is_wanted(domain2):
338 logger.debug("domain2='%s' is not wanted - EXIT!", domain2)
340 elif not instances.is_registered(domain2):
341 logger.info("components.netloc='%s' is not registered, adding ...", components.netloc)
342 instances.add(domain2, domain, "redirect_target")
344 message = f"Redirect from domain='{domain}' to response.url='{response.url}'"
345 instances.set_last_error(domain, message)
346 instances.set_software(domain, None)
347 instances.set_detection_mode(domain, None)
348 instances.set_nodeinfo_url(domain, None)
350 raise requests.exceptions.TooManyRedirects(message)
352 logger.debug("software[]='%s'", type(software))
353 if isinstance(software, str) and software == "":
354 logger.debug("Corrected empty string to None for software of domain='%s'", domain)
356 elif isinstance(software, str) and ("." in software or " " in software):
357 logger.debug("software='%s' may contain a version number, domain='%s', removing it ...", software, domain)
358 software = version.remove(software)
360 logger.debug("software[]='%s'", type(software))
361 if isinstance(software, str) and "powered by " in software:
362 logger.debug("software='%s' has 'powered by' in it", software)
363 software = version.remove(software_helper.strip_powered_by(software))
364 elif isinstance(software, str) and " hosted on " in software:
365 logger.debug("software='%s' has 'hosted on' in it", software)
366 software = version.remove(software_helper.strip_hosted_on(software))
367 elif isinstance(software, str) and " by " in software:
368 logger.debug("software='%s' has ' by ' in it", software)
369 software = software_helper.strip_until(software, " by ")
370 elif isinstance(software, str) and " see " in software:
371 logger.debug("software='%s' has ' see ' in it", software)
372 software = software_helper.strip_until(software, " see ")
374 logger.debug("software='%s' - EXIT!", software)
377 def determine_software(domain: str, path: str = None) -> str:
378 logger.debug("domain='%s',path='%s' - CALLED!", domain, path)
379 domain_helper.raise_on(domain)
381 if not isinstance(path, str) and path is not None:
382 raise ValueError(f"Parameter path[]='{type(path)}' is not of type 'str'")
384 logger.debug("Fetching nodeinfo from domain='%s',path='%s' ...", domain, path)
385 data = nodeinfo.fetch(domain, path)
388 logger.debug("data[%s]='%s'", type(data), data)
389 if "exception" in data:
390 # Continue raising it
391 logger.debug("data()=%d contains exception='%s' - raising ...", len(data), type(data["exception"]))
392 raise data["exception"]
393 elif "error_message" in data:
394 logger.debug("Returned error_message during fetching nodeinfo: '%s',status_code=%d", data['error_message'], data['status_code'])
395 software = fetch_generator_from_path(domain)
396 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
398 logger.debug("domain='%s',path='%s',data[json] found ...", domain, path)
401 logger.debug("Auto-detection for domain='%s' was failing, fetching / ...", domain)
402 software = fetch_generator_from_path(domain)
403 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
405 if "status" in data and data["status"] == "error" and "message" in data:
406 logger.warning("JSON response is an error: '%s' - Resetting detection_mode,nodeinfo_url ...", data["message"])
407 instances.set_last_error(domain, data["message"])
408 instances.set_detection_mode(domain, None)
409 instances.set_nodeinfo_url(domain, None)
410 software = fetch_generator_from_path(domain)
411 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
412 elif "software" in data and "name" in data["software"]:
413 logger.debug("Found data[json][software][name] in JSON response")
414 software = data["software"]["name"]
415 logger.debug("software[%s]='%s' - FOUND!", type(software), software)
416 elif "message" in data:
417 logger.warning("JSON response contains only a message: '%s' - Resetting detection_mode,nodeinfo_url ...", data["message"])
418 instances.set_last_error(domain, data["message"])
419 instances.set_detection_mode(domain, None)
420 instances.set_nodeinfo_url(domain, None)
422 logger.debug("Invoking fetch_generator_from_path(%s) ...", domain)
423 software = fetch_generator_from_path(domain)
424 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
425 elif "server" in data and "software" in data["server"]:
426 logger.debug("Found data[server][software]='%s' for domain='%s'", data["server"]["software"].lower(), domain)
427 software = data["server"]["software"].lower()
428 logger.debug("Detected software for domain='%s' is: '%s'", domain, software)
429 elif "software" not in data or "name" not in data["software"]:
430 logger.debug("JSON response from domain='%s' does not include [software][name] - Resetting detection_mode,nodeinfo_url ...", domain)
431 instances.set_detection_mode(domain, None)
432 instances.set_nodeinfo_url(domain, None)
434 logger.debug("Invoking fetch_generator_from_path(%s) ...", domain)
435 software = fetch_generator_from_path(domain)
436 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
438 logger.debug("software[%s]='%s'", type(software), software)
439 if software is None or software == "":
440 logger.debug("Returning None - EXIT!")
443 logger.debug("software='%s'- BEFORE!", software)
444 software = software_helper.alias(software)
445 logger.debug("software['%s']='%s' - AFTER!", type(software), software)
447 if str(software) == "":
448 logger.debug("software for domain='%s' was not detected, trying generator ...", domain)
449 software = fetch_generator_from_path(domain)
450 elif len(str(software)) > 0 and ("." in software or " " in software):
451 logger.debug("software='%s' may contain a version number, domain='%s', removing it ...", software, domain)
452 software = version.remove(software)
454 logger.debug("software[]='%s'", type(software))
455 if isinstance(software, str) and "powered by" in software:
456 logger.debug("software='%s' has 'powered by' in it", software)
457 software = version.remove(software_helper.strip_powered_by(software))
459 software = software.strip()
461 logger.debug("software='%s' - EXIT!", software)
464 def find_domains(tag: bs4.element.Tag) -> list:
465 logger.debug("tag[]='%s' - CALLED!", type(tag))
466 if not isinstance(tag, bs4.element.Tag):
467 raise ValueError(f"Parameter tag[]='{type(tag)}' is not type of bs4.element.Tag")
468 elif len(tag.select("tr")) == 0:
469 raise KeyError("No table rows found in table!")
472 for element in tag.select("tr"):
473 logger.debug("element[]='%s'", type(element))
474 if not element.find("td"):
475 logger.debug("Skipping element, no <td> found")
478 domain = tidyup.domain(element.find("td").text)
479 reason = tidyup.reason(element.findAll("td")[1].text)
481 logger.debug("domain='%s',reason='%s'", domain, reason)
483 if not domain_helper.is_wanted(domain):
484 logger.debug("domain='%s' is blacklisted - SKIPPED!", domain)
486 elif domain == "gab.com/.ai, develop.gab.com":
487 logger.debug("Multiple domains detected in one row")
497 "domain": "develop.gab.com",
501 elif not validators.domain(domain.split("/")[0]):
502 logger.warning("domain='%s' is not a valid domain - SKIPPED!", domain)
505 logger.debug("Adding domain='%s',reason='%s' ...", domain, reason)
511 logger.debug("domains()=%d - EXIT!", len(domains))
514 def add_peers(rows: dict) -> list:
515 logger.debug("rows[]='%s' - CALLED!", type(rows))
516 if not isinstance(rows, dict):
517 raise ValueError(f"Parameter rows[]='{type(rows)}' is not of type 'dict'")
520 for key in ["linked", "allowed", "blocked"]:
521 logger.debug("Checking key='%s'", key)
522 if key not in rows or rows[key] is None:
523 logger.debug("Cannot find key='%s' or it is NoneType - SKIPPED!", key)
526 logger.debug("Adding %d peer(s) to peers list ...", len(rows[key]))
527 for peer in rows[key]:
528 logger.debug("peer[%s]='%s' - BEFORE!", type(peer), peer)
529 if peer is None or peer == "":
530 logger.debug("peer is empty - SKIPPED")
532 elif isinstance(peer, dict) and "domain" in peer:
533 logger.debug("peer[domain]='%s'", peer["domain"])
534 peer = tidyup.domain(peer["domain"])
535 elif isinstance(peer, str):
536 logger.debug("peer='%s'", peer)
537 peer = tidyup.domain(peer)
539 raise ValueError(f"peer[]='{type(peer)}' is not supported,key='{key}'")
541 logger.debug("peer[%s]='%s' - AFTER!", type(peer), peer)
542 if not domain_helper.is_wanted(peer):
543 logger.debug("peer='%s' is not wanted - SKIPPED!", peer)
546 logger.debug("Appending peer='%s' ...", peer)
549 logger.debug("peers()=%d - EXIT!", len(peers))
552 def fetch_blocks(domain: str) -> list:
553 logger.debug("domain='%s' - CALLED!", domain)
554 domain_helper.raise_on(domain)
556 if not instances.is_registered(domain):
557 raise Exception(f"domain='{domain}' is not registered but function is invoked.")
562 # No CSRF by default, you don't have to add network.api_headers by yourself here
566 logger.debug("Checking CSRF for domain='%s'", domain)
567 headers = csrf.determine(domain, dict())
568 except network.exceptions as exception:
569 logger.warning("Exception '%s' during checking CSRF (fetch_blocks,%s)", type(exception), __name__)
570 instances.set_last_error(domain, exception)
572 logger.debug("Returning empty list ... - EXIT!")
576 # json endpoint for newer mastodongs
577 logger.info("Fetching domain_blocks from domain='%s' ...", domain)
578 data = network.get_json_api(
580 "/api/v1/instance/domain_blocks",
582 timeout=(config.get("connection_timeout"), config.get("read_timeout"))
586 logger.debug("data[]='%s'", type(data))
587 if "error_message" in data:
588 logger.debug("Was not able to fetch domain_blocks from domain='%s': status_code=%d,error_message='%s'", domain, data['status_code'], data['error_message'])
589 instances.set_last_error(domain, data)
591 elif "json" in data and "error" in data["json"]:
592 logger.warning("JSON API returned error message: '%s'", data["json"]["error"])
593 instances.set_last_error(domain, data)
599 logger.debug("Marking domain='%s' as successfully handled ...", domain)
600 instances.set_success(domain)
602 logger.debug("rows[%s]()=%d", type(rows), len(rows))
604 logger.debug("Checking %d entries from domain='%s' ...", len(rows), domain)
607 logger.debug("block[]='%s'", type(block))
608 if not isinstance(block, dict):
609 logger.debug("block[]='%s' is of type 'dict' - SKIPPED!", type(block))
611 elif "domain" not in block:
612 logger.warning("block()=%d does not contain element 'domain' - SKIPPED!", len(block))
614 elif "severity" not in block:
615 logger.warning("block()=%d does not contain element 'severity' - SKIPPED!", len(block))
617 elif block["severity"] in ["accept", "accepted"]:
618 logger.debug("block[domain]='%s' has unwanted severity level '%s' - SKIPPED!", block["domain"], block["severity"])
620 elif "digest" in block and not validators.hashes.sha256(block["digest"]):
621 logger.warning("block[domain]='%s' has invalid block[digest]='%s' - SKIPPED!", block["domain"], block["digest"])
624 reason = tidyup.reason(block["comment"]) if "comment" in block and block["comment"] is not None and block["comment"] != "" else None
626 logger.debug("Appending blocker='%s',blocked='%s',reason='%s',block_level='%s'", domain, block["domain"], reason, block["severity"])
629 "blocked" : block["domain"],
630 "digest" : block["digest"] if "digest" in block else None,
632 "block_level": blocks.alias_block_level(block["severity"]),
635 logger.debug("domain='%s' has no block list", domain)
637 except network.exceptions as exception:
638 logger.warning("domain='%s',exception[%s]='%s'", domain, type(exception), str(exception))
639 instances.set_last_error(domain, exception)
641 logger.debug("blocklist()=%d - EXIT!", len(blocklist))