templates_blog_modulesdir = $(templates_blogdir)/modules
templates_dnsbl_DATA = \
- src/templates/dnsbl/index.html
+ src/templates/dnsbl/index.html \
+ src/templates/dnsbl/search.html
templates_dnsbldir = $(templatesdir)/dnsbl
#!/usr/bin/python3
+import asyncio
import datetime
import json
import logging
# Return the report
return Report(self.backend, **response)
+ # Search!
+
+ async def search(self, q):
+ """
+ Searches for a domain
+ """
+ tasks = {}
+
+ # Perform the search
+ result = await self._fetch("/search", args={ "q" : q })
+
+ # Fetch all lists
+ async with asyncio.TaskGroup() as tg:
+ for slug in result:
+ task = tg.create_task(
+ self.get_list(slug),
+ )
+
+ tasks[task] = result[slug]
+
+ # Return a mapping with the list and domains
+ return { task.result() : tasks[task] for task in tasks }
+
class Model(pydantic.BaseModel):
"""
def __str__(self):
return self.name
+ def __hash__(self):
+ return hash(self.slug)
+
# Name
name : str
{{ _("Report") }}
</a>
+ <div class="navbar-item">
+ <form action="/dnsbl/search" method="GET">
+ <div class="field">
+ <div class="control has-icons-left">
+ <input class="input" type="text"
+ name="q" {% if q %}value="{{ q }}"{% end %}
+ placeholder="{{ _("Search DNSBL...") }}">
+ <span class="icon is-small is-left">
+ <i class="fas fa-search"></i>
+ </span>
+ </div>
+ </div>
+ </form>
+ </div>
+
{# Location #}
{% elif request.path.startswith("/location") %}
<a class="navbar-item is-tab
--- /dev/null
+{% extends "../base.html" %}
+
+{% block head %}
+ {% module OpenGraph(
+ title=_("IPFire DNSBL - Search Results For: %s") % q,
+ ) %}
+{% end block %}
+
+{% block title %}{{ _("Search Results For: %s") % q }}{% end block %}
+
+{% block container %}
+ <section class="hero is-dark">
+ <div class="hero-body">
+ <div class="container">
+ <h1 class="title">
+ {{ _("Search Results For: %s") % q }}
+ </h1>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ {# Show any results #}
+ {% if results %}
+ {% for list, domains in sorted(results.items()) %}
+ <div class="block">
+ <h5 class="title is-5">
+ <a href="/dnsbl/lists/{{ list.slug }}">
+ {{ list }}
+ </a>
+ </h5>
+
+ <div class="content">
+ {{ _("There is one matching domain on this list:",
+ "There are %(num)s matching domains on this list:", len(domains)) % \
+ { "num" : len(domains) } }}
+
+ <ul>
+ {% for domain in domains %}
+ <li>
+ {{ domain }}
+ </li>
+ {% end %}
+ </ul>
+ </div>
+ </div>
+ {% end %}
+
+ {# Show a note if there has been no results #}
+ {% else %}
+ <div class="notification has-text-centered">
+ {{ _("There are no matches for '%s'") % q }}
+ </div>
+ {% end %}
+ </div>
+ </section>
+{% end block %}
(r"/dnsbl/lists/(\w+)/reports", dnsbl.ListReportsHandler),
(r"/dnsbl/report", dnsbl.SubmitReportHandler),
(r"/dnsbl/reports/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", dnsbl.ReportHandler),
+ (r"/dnsbl/search", dnsbl.SearchHandler),
# Single-Sign-On for Discourse
(r"/sso/discourse", auth.SSODiscourse),
self.render("dnsbl/reports/show.html", report=report, list=list)
+class SearchHandler(base.AnalyticsMixin, BaseHandler):
+ async def get(self):
+ # Fetch the query
+ q = self.get_argument("q", None)
+
+ # Fail if there is nothing to search for
+ if not q:
+ raise tornado.web.HTTPError(400, "Empty search query")
+
+ # Search for the query
+ results = await self.backend.dnsbl.search(q)
+
+ # Render the page
+ self.render("dnsbl/search.html", q=q, results=results)
+
+
class ListsModule(ui_modules.UIModule):
def render(self, lists):
return self.render_string("dnsbl/modules/lists.html", lists=lists)