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:
99 logger.debug("Fetching instances for domain='%s',software='%s',origin='%s'", domain, software, origin)
100 peerlist = fetch_peers(domain, software, origin)
102 logger.debug("peerlist[]='%s'", type(peerlist))
103 if isinstance(peerlist, list):
104 logger.debug("Invoking instances.set_total_peerlist(%s,%d) ...", domain, len(peerlist))
105 instances.set_total_peers(domain, peerlist)
107 logger.debug("Invoking cookies.clear(%s) ...", domain)
108 cookies.clear(domain)
110 logger.debug("peerlist[]='%s'", type(peerlist))
112 logger.warning("Cannot fetch peers: domain='%s',software='%s'", domain, software)
114 if instances.has_pending(domain):
115 logger.debug("Flushing updates for domain='%s' ...", domain)
116 instances.update(domain)
119 logger.debug("EXIT!")
121 elif len(peerlist) == 0:
122 logger.info("domain='%s' returned an empty peer list.", domain)
124 if instances.has_pending(domain):
125 logger.debug("Flushing updates for domain='%s' ...", domain)
126 instances.update(domain)
129 logger.debug("domain='%s',software='%s' has an empty peer list returned - EXIT!", domain, software)
132 logger.info("Checking %d instance(s) from domain='%s',software='%s',depth=%d ...", len(peerlist), domain, software, _DEPTH)
133 for instance in peerlist:
134 logger.debug("instance='%s'", instance)
135 if instance is None or instance == "":
136 logger.debug("instance[%s]='%s' is either None or empty - SKIPPED!", type(instance), instance)
139 logger.debug("instance='%s' - BEFORE!", instance)
140 instance = tidyup.domain(instance) if isinstance(instance, str) and instance != "" else None
141 logger.debug("instance='%s' - AFTER!", instance)
143 if instance is None or instance == "":
144 logger.warning("instance='%s' is empty after tidyup.domain(), domain='%s'", instance, domain)
146 elif ".." in instance:
147 logger.warning("instance='%s' contains double-dot, removing ...", instance)
148 instance = instance.replace("..", ".")
150 logger.debug("instance='%s' - BEFORE!", instance)
151 instance = instance.encode("idna").decode("utf-8")
152 logger.debug("instance='%s' - AFTER!", instance)
154 if not domain_helper.is_wanted(instance):
155 logger.debug("instance='%s' is not wanted - SKIPPED!", instance)
157 elif instance.find("/profile/") > 0 or instance.find("/users/") > 0 or (instances.is_registered(instance.split("/")[0]) and instance.find("/c/") > 0):
158 logger.debug("instance='%s' is a link to a single user profile - SKIPPED!", instance)
160 elif instance.find("/tag/") > 0:
161 logger.debug("instance='%s' is a link to a tag - SKIPPED!", instance)
163 elif not instances.is_registered(instance):
164 logger.debug("Checking if domain='%s' has pending updates ...", domain)
165 if instances.has_pending(domain):
166 logger.debug("Flushing updates for domain='%s' ...", domain)
167 instances.update(domain)
169 logger.debug("instance='%s',origin='%s',_DEPTH=%d reached!", instance, origin, _DEPTH)
170 if _DEPTH <= config.get("max_crawl_depth") and len(peerlist) >= config.get("min_peers_length"):
171 logger.debug("Fetching instance='%s',origin='%s',command='%s',path='%s',_DEPTH=%d ...", instance, domain, command, path, _DEPTH)
172 fetch_instances(instance, domain, None, command, path)
174 logger.debug("Adding instance='%s',domain='%s',command='%s',_DEPTH=%d ...", instance, domain, command, _DEPTH)
175 instances.add(instance, domain, command)
177 logger.debug("Checking if domain='%s' has pending updates ...", domain)
178 if instances.has_pending(domain):
179 logger.debug("Flushing updates for domain='%s' ...", domain)
180 instances.update(domain)
183 logger.debug("EXIT!")
185 def fetch_peers(domain: str, software: str, origin: str) -> list:
186 logger.debug("domain='%s',software='%s',origin='%s' - CALLED!", domain, software, origin)
187 domain_helper.raise_on(domain)
189 if not isinstance(software, str) and software is not None:
190 raise ValueError(f"Parameter software[]='{type(software)}' is not of type 'str'")
191 elif isinstance(software, str) and software == "":
192 raise ValueError("Parameter 'software' is empty")
193 elif software_helper.is_relay(software):
194 raise ValueError(f"domain='{domain}' is of software='{software}' and isn't supported here.")
195 elif not isinstance(origin, str) and origin is not None:
196 raise ValueError(f"Parameter origin[]='{type(origin)}' is not of type 'str'")
197 elif isinstance(origin, str) and origin == "":
198 raise ValueError("Parameter 'origin' is empty")
200 if software == "misskey":
201 logger.debug("Invoking misskey.fetch_peers(%s) ...", domain)
202 return misskey.fetch_peers(domain)
203 elif software == "lemmy":
204 logger.debug("Invoking lemmy.fetch_peers(%s,%s) ...", domain, origin)
205 return lemmy.fetch_peers(domain, origin)
206 elif software == "peertube":
207 logger.debug("Invoking peertube.fetch_peers(%s) ...", domain)
208 return peertube.fetch_peers(domain)
210 # No CSRF by default, you don't have to add network.api_headers by yourself here
214 logger.debug("Checking CSRF for domain='%s'", domain)
215 headers = csrf.determine(domain, dict())
216 except network.exceptions as exception:
217 logger.warning("Exception '%s' during checking CSRF (fetch_peers,%s)", type(exception), __name__)
218 instances.set_last_error(domain, exception)
220 logger.debug("Returning empty list ... - EXIT!")
224 "/api/v1/instance/peers",
228 # Init peers variable
231 logger.debug("Checking %d paths ...", len(paths))
233 logger.debug("Fetching path='%s' from domain='%s',software='%s' ...", path, domain, software)
234 data = network.get_json_api(
238 timeout=(config.get("connection_timeout"), config.get("read_timeout"))
241 logger.debug("data[]='%s'", type(data))
242 if "error_message" in data:
243 logger.debug("Was not able to fetch peers from path='%s',domain='%s' ...", path, domain)
244 instances.set_last_error(domain, data)
245 elif "json" in data and len(data["json"]) > 0:
246 logger.debug("Querying API path='%s' was successful: domain='%s',data[json][%s]()=%d", path, domain, type(data['json']), len(data['json']))
249 logger.debug("Marking domain='%s' as successfully handled ...", domain)
250 instances.set_success(domain)
253 if not isinstance(peers, list):
254 logger.warning("peers[]='%s' is not of type 'list', maybe bad API response?", type(peers))
257 logger.debug("Invoking instances.set_total_peers(%s,%d) ...", domain, len(peers))
258 instances.set_total_peers(domain, peers)
260 logger.debug("peers()=%d - EXIT!", len(peers))
263 def fetch_generator_from_path(domain: str, path: str = "/") -> str:
264 logger.debug("domain='%s',path='%s' - CALLED!", domain, path)
265 domain_helper.raise_on(domain)
267 if not isinstance(path, str):
268 raise ValueError(f"path[]='{type(path)}' is not of type 'str'")
270 raise ValueError("Parameter 'path' is empty")
274 logger.debug("Fetching path='%s' from domain='%s' ...", path, domain)
275 response = network.fetch_response(
278 headers=network.web_headers,
279 timeout=(config.get("connection_timeout"), config.get("read_timeout")),
283 logger.debug("response.ok='%s',response.status_code=%d,response.text()=%d", response.ok, response.status_code, len(response.text))
284 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):
285 logger.debug("Parsing response.text()=%d Bytes ...", len(response.text))
286 doc = bs4.BeautifulSoup(response.text, "html.parser")
288 logger.debug("doc[]='%s'", type(doc))
289 platform = doc.find("meta", {"property": "og:platform"})
290 generator = doc.find("meta", {"name" : "generator"})
291 site_name = doc.find("meta", {"property": "og:site_name"})
292 app_name = doc.find("meta", {"name" : "application-name"})
294 logger.debug("generator[]='%s',site_name[]='%s',platform[]='%s',app_name[]='%s'", type(generator), type(site_name), type(platform), type(app_name))
295 if isinstance(platform, bs4.element.Tag) and isinstance(platform.get("content"), str) and platform.get("content") != "":
296 logger.debug("Found property=og:platform, domain='%s'", domain)
297 software = tidyup.domain(platform.get("content"))
299 logger.debug("software[%s]='%s'", type(software), software)
300 if software is not None and software != "":
301 logger.debug("domain='%s' has og:platform='%s' - Setting detection_mode=PLATFORM ...", domain, software)
302 instances.set_detection_mode(domain, "PLATFORM")
303 elif isinstance(generator, bs4.element.Tag) and isinstance(generator.get("content"), str) and generator.get("content") != "":
304 logger.debug("Found generator meta tag: domain='%s'", domain)
305 software = tidyup.domain(generator.get("content"))
307 logger.debug("software[%s]='%s'", type(software), software)
308 if software is not None and software != "":
309 logger.info("domain='%s' is generated by software='%s' - Setting detection_mode=GENERATOR ...", domain, software)
310 instances.set_detection_mode(domain, "GENERATOR")
311 elif isinstance(app_name, bs4.element.Tag) and isinstance(app_name.get("content"), str) and app_name.get("content") != "":
312 logger.debug("Found property=og:app_name, domain='%s'", domain)
313 software = tidyup.domain(app_name.get("content"))
315 logger.debug("software[%s]='%s'", type(software), software)
316 if software is not None and software != "":
317 logger.debug("domain='%s' has application-name='%s' - Setting detection_mode=app_name ...", domain, software)
318 instances.set_detection_mode(domain, "APP_NAME")
319 elif isinstance(site_name, bs4.element.Tag) and isinstance(site_name.get("content"), str) and site_name.get("content") != "":
320 logger.debug("Found property=og:site_name, domain='%s'", domain)
321 software = tidyup.domain(site_name.get("content"))
323 logger.debug("software[%s]='%s'", type(software), software)
324 if software is not None and software != "":
325 logger.debug("domain='%s' has og:site_name='%s' - Setting detection_mode=SITE_NAME ...", domain, software)
326 instances.set_detection_mode(domain, "SITE_NAME")
327 elif not domain_helper.is_in_url(domain, response.url):
328 logger.warning("domain='%s' doesn't match response.url='%s', maybe redirect to other domain?", domain, response.url)
330 components = urlparse(response.url)
331 domain2 = components.netloc.lower().split(":")[0]
333 logger.debug("domain2='%s'", domain2)
334 if not domain_helper.is_wanted(domain2):
335 logger.debug("domain2='%s' is not wanted - EXIT!", domain2)
337 elif not instances.is_registered(domain2):
338 logger.info("components.netloc='%s' is not registered, adding ...", components.netloc)
339 instances.add(domain2, domain, "redirect_target")
341 message = f"Redirect from domain='{domain}' to response.url='{response.url}'"
342 instances.set_last_error(domain, message)
343 instances.set_software(domain, None)
344 instances.set_detection_mode(domain, None)
345 instances.set_nodeinfo_url(domain, None)
347 raise requests.exceptions.TooManyRedirects(message)
349 logger.debug("software[]='%s'", type(software))
350 if isinstance(software, str) and software == "":
351 logger.debug("Corrected empty string to None for software of domain='%s'", domain)
353 elif isinstance(software, str) and ("." in software or " " in software):
354 logger.debug("software='%s' may contain a version number, domain='%s', removing it ...", software, domain)
355 software = version.remove(software)
357 logger.debug("software[]='%s'", type(software))
358 if isinstance(software, str) and "powered by " in software:
359 logger.debug("software='%s' has 'powered by' in it", software)
360 software = version.remove(software_helper.strip_powered_by(software))
361 elif isinstance(software, str) and " hosted on " in software:
362 logger.debug("software='%s' has 'hosted on' in it", software)
363 software = version.remove(software_helper.strip_hosted_on(software))
364 elif isinstance(software, str) and " by " in software:
365 logger.debug("software='%s' has ' by ' in it", software)
366 software = software_helper.strip_until(software, " by ")
367 elif isinstance(software, str) and " see " in software:
368 logger.debug("software='%s' has ' see ' in it", software)
369 software = software_helper.strip_until(software, " see ")
371 logger.debug("software='%s' - EXIT!", software)
374 def determine_software(domain: str, path: str = None) -> str:
375 logger.debug("domain='%s',path='%s' - CALLED!", domain, path)
376 domain_helper.raise_on(domain)
378 if not isinstance(path, str) and path is not None:
379 raise ValueError(f"Parameter path[]='{type(path)}' is not of type 'str'")
381 logger.debug("Fetching nodeinfo from domain='%s',path='%s' ...", domain, path)
382 data = nodeinfo.fetch(domain, path)
385 logger.debug("data[%s]='%s'", type(data), data)
386 if "exception" in data:
387 # Continue raising it
388 logger.debug("data()=%d contains exception='%s' - raising ...", len(data), type(data["exception"]))
389 raise data["exception"]
390 elif "error_message" in data:
391 logger.debug("Returned error_message during fetching nodeinfo: '%s',status_code=%d", data['error_message'], data['status_code'])
392 software = fetch_generator_from_path(domain)
393 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
395 logger.debug("domain='%s',path='%s',data[json] found ...", domain, path)
398 logger.debug("Auto-detection for domain='%s' was failing, fetching / ...", domain)
399 software = fetch_generator_from_path(domain)
400 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
402 if "status" in data and data["status"] == "error" and "message" in data:
403 logger.warning("JSON response is an error: '%s' - Resetting detection_mode,nodeinfo_url ...", data["message"])
404 instances.set_last_error(domain, data["message"])
405 instances.set_detection_mode(domain, None)
406 instances.set_nodeinfo_url(domain, None)
407 software = fetch_generator_from_path(domain)
408 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
409 elif "software" in data and "name" in data["software"]:
410 logger.debug("Found data[json][software][name] in JSON response")
411 software = data["software"]["name"]
412 logger.debug("software[%s]='%s' - FOUND!", type(software), software)
413 elif "message" in data:
414 logger.warning("JSON response contains only a message: '%s' - Resetting detection_mode,nodeinfo_url ...", data["message"])
415 instances.set_last_error(domain, data["message"])
416 instances.set_detection_mode(domain, None)
417 instances.set_nodeinfo_url(domain, None)
419 logger.debug("Invoking fetch_generator_from_path(%s) ...", domain)
420 software = fetch_generator_from_path(domain)
421 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
422 elif "server" in data and "software" in data["server"]:
423 logger.debug("Found data[server][software]='%s' for domain='%s'", data["server"]["software"].lower(), domain)
424 software = data["server"]["software"].lower()
425 logger.debug("Detected software for domain='%s' is: '%s'", domain, software)
426 elif "software" not in data or "name" not in data["software"]:
427 logger.debug("JSON response from domain='%s' does not include [software][name] - Resetting detection_mode,nodeinfo_url ...", domain)
428 instances.set_detection_mode(domain, None)
429 instances.set_nodeinfo_url(domain, None)
431 logger.debug("Invoking fetch_generator_from_path(%s) ...", domain)
432 software = fetch_generator_from_path(domain)
433 logger.debug("Generator for domain='%s' is: '%s'", domain, software)
435 logger.debug("software[%s]='%s'", type(software), software)
436 if software is None or software == "":
437 logger.debug("Returning None - EXIT!")
440 logger.debug("software='%s'- BEFORE!", software)
441 software = software_helper.alias(software)
442 logger.debug("software['%s']='%s' - AFTER!", type(software), software)
444 if str(software) == "":
445 logger.debug("software for domain='%s' was not detected, trying generator ...", domain)
446 software = fetch_generator_from_path(domain)
447 elif len(str(software)) > 0 and ("." in software or " " in software):
448 logger.debug("software='%s' may contain a version number, domain='%s', removing it ...", software, domain)
449 software = version.remove(software)
451 logger.debug("software[]='%s'", type(software))
452 if isinstance(software, str) and "powered by" in software:
453 logger.debug("software='%s' has 'powered by' in it", software)
454 software = version.remove(software_helper.strip_powered_by(software))
456 software = software.strip()
458 logger.debug("software='%s' - EXIT!", software)
461 def find_domains(tag: bs4.element.Tag) -> list:
462 logger.debug("tag[]='%s' - CALLED!", type(tag))
463 if not isinstance(tag, bs4.element.Tag):
464 raise ValueError(f"Parameter tag[]='{type(tag)}' is not type of bs4.element.Tag")
465 elif len(tag.select("tr")) == 0:
466 raise KeyError("No table rows found in table!")
469 for element in tag.select("tr"):
470 logger.debug("element[]='%s'", type(element))
471 if not element.find("td"):
472 logger.debug("Skipping element, no <td> found")
475 domain = tidyup.domain(element.find("td").text)
476 reason = tidyup.reason(element.findAll("td")[1].text)
478 logger.debug("domain='%s',reason='%s'", domain, reason)
480 if not domain_helper.is_wanted(domain):
481 logger.debug("domain='%s' is blacklisted - SKIPPED!", domain)
483 elif domain == "gab.com/.ai, develop.gab.com":
484 logger.debug("Multiple domains detected in one row")
494 "domain": "develop.gab.com",
498 elif not validators.domain(domain.split("/")[0]):
499 logger.warning("domain='%s' is not a valid domain - SKIPPED!", domain)
502 logger.debug("Adding domain='%s',reason='%s' ...", domain, reason)
508 logger.debug("domains()=%d - EXIT!", len(domains))
511 def add_peers(rows: dict) -> list:
512 logger.debug("rows[]='%s' - CALLED!", type(rows))
513 if not isinstance(rows, dict):
514 raise ValueError(f"Parameter rows[]='{type(rows)}' is not of type 'dict'")
517 for key in ["linked", "allowed", "blocked"]:
518 logger.debug("Checking key='%s'", key)
519 if key not in rows or rows[key] is None:
520 logger.debug("Cannot find key='%s' or it is NoneType - SKIPPED!", key)
523 logger.debug("Adding %d peer(s) to peers list ...", len(rows[key]))
524 for peer in rows[key]:
525 logger.debug("peer[%s]='%s' - BEFORE!", type(peer), peer)
526 if peer is None or peer == "":
527 logger.debug("peer is empty - SKIPPED")
529 elif isinstance(peer, dict) and "domain" in peer:
530 logger.debug("peer[domain]='%s'", peer["domain"])
531 peer = tidyup.domain(peer["domain"])
532 elif isinstance(peer, str):
533 logger.debug("peer='%s'", peer)
534 peer = tidyup.domain(peer)
536 raise ValueError(f"peer[]='{type(peer)}' is not supported,key='{key}'")
538 logger.debug("peer[%s]='%s' - AFTER!", type(peer), peer)
539 if not domain_helper.is_wanted(peer):
540 logger.debug("peer='%s' is not wanted - SKIPPED!", peer)
543 logger.debug("Appending peer='%s' ...", peer)
546 logger.debug("peers()=%d - EXIT!", len(peers))
549 def fetch_blocks(domain: str) -> list:
550 logger.debug("domain='%s' - CALLED!", domain)
551 domain_helper.raise_on(domain)
553 if not instances.is_registered(domain):
554 raise Exception(f"domain='{domain}' is not registered but function is invoked.")
559 # No CSRF by default, you don't have to add network.api_headers by yourself here
563 logger.debug("Checking CSRF for domain='%s'", domain)
564 headers = csrf.determine(domain, dict())
565 except network.exceptions as exception:
566 logger.warning("Exception '%s' during checking CSRF (fetch_blocks,%s)", type(exception), __name__)
567 instances.set_last_error(domain, exception)
569 logger.debug("Returning empty list ... - EXIT!")
573 # json endpoint for newer mastodongs
574 logger.info("Fetching domain_blocks from domain='%s' ...", domain)
575 data = network.get_json_api(
577 "/api/v1/instance/domain_blocks",
579 timeout=(config.get("connection_timeout"), config.get("read_timeout"))
583 logger.debug("data[]='%s'", type(data))
584 if "error_message" in data:
585 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'])
586 instances.set_last_error(domain, data)
588 elif "json" in data and "error" in data["json"]:
589 logger.warning("JSON API returned error message: '%s'", data["json"]["error"])
590 instances.set_last_error(domain, data)
596 logger.debug("Marking domain='%s' as successfully handled ...", domain)
597 instances.set_success(domain)
599 logger.debug("rows[%s]()=%d", type(rows), len(rows))
601 logger.debug("Checking %d entries from domain='%s' ...", len(rows), domain)
604 logger.debug("block[]='%s'", type(block))
605 if not isinstance(block, dict):
606 logger.debug("block[]='%s' is of type 'dict' - SKIPPED!", type(block))
608 elif "domain" not in block:
609 logger.warning("block()=%d does not contain element 'domain' - SKIPPED!", len(block))
611 elif "severity" not in block:
612 logger.warning("block()=%d does not contain element 'severity' - SKIPPED!", len(block))
614 elif block["severity"] in ["accept", "accepted"]:
615 logger.debug("block[domain]='%s' has unwanted severity level '%s' - SKIPPED!", block["domain"], block["severity"])
617 elif "digest" in block and not validators.hashes.sha256(block["digest"]):
618 logger.warning("block[domain]='%s' has invalid block[digest]='%s' - SKIPPED!", block["domain"], block["digest"])
621 reason = tidyup.reason(block["comment"]) if "comment" in block and block["comment"] is not None and block["comment"] != "" else None
623 logger.debug("Appending blocker='%s',blocked='%s',reason='%s',block_level='%s'", domain, block["domain"], reason, block["severity"])
626 "blocked" : block["domain"],
627 "digest" : block["digest"] if "digest" in block else None,
629 "block_level": blocks.alias_block_level(block["severity"]),
632 logger.debug("domain='%s' has no block list", domain)
634 except network.exceptions as exception:
635 logger.warning("domain='%s',exception[%s]='%s'", domain, type(exception), str(exception))
636 instances.set_last_error(domain, exception)
638 logger.debug("blocklist()=%d - EXIT!", len(blocklist))