This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git

commit 8a3e86518ac7561171ec7f4f2a62bcaee109b319
Author: Daniel Gruno <[email protected]>
AuthorDate: Sun Sep 6 19:14:30 2020 +0200

    add defuzzer plugin
---
 server/plugins/defuzzer.py | 175 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 175 insertions(+)

diff --git a/server/plugins/defuzzer.py b/server/plugins/defuzzer.py
new file mode 100644
index 0000000..8d3e66c
--- /dev/null
+++ b/server/plugins/defuzzer.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import calendar
+import re
+import shlex
+import typing
+
+"""
+This is the query de-fuzzer library for Foal.
+It turns a URL search query into an ES query
+
+"""
+
+
+def defuzz(formdata: dict, nodate: bool = False) -> dict:
+    # Default to 30 day date range
+    daterange = {"gt": "now-30d", "lt": "now+1d"}
+
+    # Custom date range?
+    # If a month is the only thing, fake start and end
+    if "date" in formdata and "e" not in formdata:
+        formdata["s"] = formdata["date"]
+        formdata["e"] = formdata["date"]
+    # classic start and end month params
+    if "s" in formdata and "e" in formdata:
+        syear, smonth = formdata["s"].split("-")
+        eyear, emonth = formdata["e"].split("-")
+        estart, eend = calendar.monthrange(int(eyear), int(emonth))
+        daterange = {
+            "gt": "%04u/%02u/01 00:00:00" % (int(syear), int(smonth)),
+            "lt": "%04u/%02u/%02u 23:59:59" % (int(eyear), int(emonth), eend),
+        }
+    # Advanced date formatting
+    elif "d" in formdata:
+        # The more/less than N days/weeks/months/years ago
+        m = re.match(r"^([a-z]+)=([0-9Mwyd]+)$", formdata["d"])
+        if m:
+            t = m.group(1)
+            r = m.group(2)
+            if t == "lte" and r:
+                daterange = {"gt": "now-%s" % r}
+            elif t == "gte" and r:
+                daterange = {"lt": "now-%s" % r}
+        # simple one month listing
+        m = re.match(r"^(\d\d\d\d-\d+)$", formdata["d"])
+        if m:
+            xdate = m.group(1)
+            dyear, dmonth = xdate.split("-", 1)
+            daterange = {
+                "gte": "%04u-%02u-01||/M" % (int(dyear), int(dmonth)),
+                "lte": "%04u-%02u-01||/M" % (int(dyear), int(dmonth)),
+                "format": "yyyy-MM-dd",
+            }
+
+        # dfr and dto defining a time span
+        m = re.match(r"^dfr=(\d\d\d\d-\d+-\d+)\|dto=(\d\d\d\d-\d+-\d+)$", 
formdata["d"])
+        if m:
+            dfr = m.group(1)
+            dto = m.group(2)
+            syear, smonth, sday = dfr.split("-")
+            eyear, emonth, eday = dto.split("-")
+            daterange = {
+                "gt": "%04u/%02u/%02u 00:00:00" % (int(syear), int(smonth), 
int(sday)),
+                "lt": "%04u/%02u/%02u 23:59:59" % (int(eyear), int(emonth), 
int(eday)),
+            }
+
+    # List parameter(s)
+    if "domain" in formdata:
+        fqdn = formdata["domain"]
+        listname = formdata.get("list", "*")
+    elif "list" in formdata:
+        listname, fqdn = formdata["list"].split("@", 1)
+    else:  # No domain or list at all? BORK!
+        listname = "*"
+        fqdn = "*"
+    list_raw = "<%s.%s>" % (listname, fqdn)
+
+    # Default is to look in a specific list
+    query_list_hash: typing.Dict = {"term": {"list_raw": list_raw}}
+
+    # *@fqdn match?
+    if listname == "*" and fqdn != "*":
+        query_list_hash = {"wildcard": {"list_raw": {"value": "*.%s>" % fqdn}}}
+
+    # listname@* match?
+    if listname != "*" and fqdn == "*":
+        query_list_hash = {"wildcard": {"list_raw": "<%s.*>" % listname}}
+
+    # *@* ??
+    if listname == "*" and fqdn == "*":
+        query_list_hash = {"wildcard": {"list_raw": "*"}}
+
+    must = [query_list_hash]
+    must_not = []
+
+    # Append date range if not excluded
+    if not nodate:
+        must.append({"range": {"date": daterange}})
+
+    # Query string search:
+    # - foo bar baz: find emails with these words
+    # - orange -apples: fond email with oranges but not apples
+    # - "this sentence": find emails with this exact string
+    if "q" in formdata:
+        qs = formdata["q"].replace(":", "")
+        bits = shlex.split(qs)
+
+        should = []
+        shouldnot = []
+
+        for bit in bits:
+            force_positive = False
+            # Translate -- into a positive '-', so you can find "-1" etc
+            if bit[0:1] == "--":
+                force_positive = True
+                bit = bit[1:]
+            # Negatives
+            if bit[0] == "-" and not force_positive:
+                # Quoted?
+                if bit.find(" ") != -1:
+                    bit = '"' + bit + '"'
+                shouldnot.append(bit[1:])
+            # Positives
+            else:
+                # Quoted?
+                if bit.find(" ") != -1:
+                    bit = '"' + bit + '"'
+                should.append(bit)
+
+        if len(should) > 0:
+            must.append(
+                {
+                    "multi_match": {
+                        "fields": ["subject", "from", "body"],
+                        "query": " AND ".join(should),
+                    }
+                }
+            )
+        if len(shouldnot) > 0:
+            must_not.append(
+                {
+                    "multi_match": {
+                        "fields": ["subject", "from", "body"],
+                        "query": " AND ".join(shouldnot),
+                    }
+                }
+            )
+
+    # Header parameters
+    for header in ["from", "subject", "body", "to"]:
+        hname = "header_%s" % header
+        if hname in formdata:
+            hvalue = formdata[hname]  # .replace('"', "")
+            must.append({"match": {header: {"query": hvalue}}})
+
+    thebool = {"must": must}
+
+    if len(must_not) > 0:
+        thebool["must_not"] = must_not
+    return thebool

Reply via email to