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


The following commit(s) were added to refs/heads/master by this push:
     new 2885bfb  Move older gen generators into their own file
2885bfb is described below

commit 2885bfb056ff5ce83f96c81eb9099b88a12a4658
Author: Daniel Gruno <[email protected]>
AuthorDate: Sat Sep 5 10:14:51 2020 +0200

    Move older gen generators into their own file
---
 tools/plugins/generators.py                        | 176 +-------------------
 tools/plugins/{generators.py => generators_old.py} | 177 +--------------------
 2 files changed, 7 insertions(+), 346 deletions(-)

diff --git a/tools/plugins/generators.py b/tools/plugins/generators.py
index 79ae9c9..919381c 100644
--- a/tools/plugins/generators.py
+++ b/tools/plugins/generators.py
@@ -16,15 +16,14 @@
 # limitations under the License.
 
 """
-This file contains the various ID generators for Pony Mail's archivers.
+This file contains the various newer generation ID generators for Pony Mail's 
archivers.
+For older ID generators, see generators_old.py
 """
 
 import base64
 import hashlib
-import email.utils
-import time
-import re
 import typing
+import plugins.generators_old
 
 # For optional nonce
 config: typing.Optional[dict] = None
@@ -175,175 +174,12 @@ def full(msg, _body, lid, _attachments, _raw_msg):
     return mid
 
 
-# Medium: Standard 0.9 generator - Not recommended for future installations.
-# See 'full' or 'cluster' generators instead.
-def medium(msg, body, lid, _attachments, _raw_msg):
-    """
-    Standard 0.9 generator - Not recommended for future installations.
-    (does not generate sufficiently unique ids)
-    Also the lid is included in the hash; this causes problems if the listname 
needs to be changed.
-
-    N.B. The id is not guaranteed stable - i.e. it may change if the message 
is reparsed.
-    The id depends on the parsed body, which depends on the exact method used 
to parse the mail.
-    For example, are invalid characters ignored or replaced; is html parsing 
used?
-
-    The following message fields are concatenated to form the hash input:
-    - body: if bytes as is else encoded ascii, ignoring invalid characters; if 
the body is null an Exception is thrown
-    - lid
-    - Date header if it exists and parses OK; failing that
-    - archived-at header if it exists and parses OK; failing that
-    - current time.
-    The resulting date is converted to YYYY/MM/DD HH:MM:SS (using UTC)
-
-    Parameters:
-    msg - the parsed message (used to get the date)
-    body - the parsed text content (may be null)
-    lid - list id
-    _attachments - list of attachments (not used)
-    _raw_msg - the original message bytes (not used)
-
-    Returns: "<hash>@<lid>" where hash is sha224 of the message items noted 
above
-    """
-
-    # Use text body
-    xbody = body.encode('utf-8', 'ignore')
-    # Use List ID
-    xbody += bytes(lid, encoding='ascii')
-    # Use Date header
-    try:
-        mdate = email.utils.parsedate_tz(msg.get('date'))
-    except:
-        pass
-    # In keeping with preserving the past, we have kept this next section(s).
-    # For all intents and purposes, this is not a proper way of maintaining
-    # a consistent ID in case of missing dates. It is recommended to use
-    # another generator
-    if not mdate and msg.get('archived-at'):
-        mdate = email.utils.parsedate_tz(msg.get('archived-at'))
-    elif not mdate:
-        mdate = time.gmtime()  # Get a standard 9-tuple
-        mdate = mdate + (0,)  # Fake a TZ (10th element)
-    mdatestring = time.strftime("%Y/%m/%d %H:%M:%S", 
time.gmtime(email.utils.mktime_tz(mdate)))
-    xbody += bytes(mdatestring, encoding='ascii')
-    mid = "%s@%s" % (hashlib.sha224(xbody).hexdigest(), lid)
-    return mid
-
-# cluster: Use data that is guaranteed to be the same across cluster setups
-# This is the recommended generator for cluster setups.
-# Unlike 'medium', this only makes use of the Date: header and not the 
archived-at,
-# as the archived-at may change from node to node (and will change if not in 
the raw mbox file)
-# Also the lid is not included in the hash, so the hash does not change if the 
lid is overridden
-#
-
-
-def cluster(msg, body, lid, attachments, _raw_msg):
-    """
-    Use data that is guaranteed to be the same across cluster setups
-    For mails with a valid Message-ID this is likely to be unique
-    In other cases it is better than the medium generator as it uses several 
extra fields
-
-    N.B. The id is not guaranteed stable - i.e. it may change if the message 
is reparsed.
-    The id depends on the parsed body, which depends on the exact method used 
to parse the mail.
-    For example, are invalid characters ignored or replaced; is html parsing 
used?
-
-    The following message fields are concatenated to form the hash input:
-    - body as is if bytes else encoded ascii, ignoring invalid characters; if 
the body is null it is treated as an empty string
-      (currently trailing whitespace is dropped)
-    - Message-ID (if present)
-    - Date header converted to YYYY/MM/DD HH:MM:SS (UTC)
-      or "(null)" if the date does not exist or cannot be converted
-    - sender, encoded as ascii (if the field exists)
-    - subject, encoded as ascii (if the field exists)
-    - the hashes of any attachments
-
-    Note: the lid is not included in the hash.
-
-    Parameters:
-    msg - the parsed message
-    body - the parsed text content
-    lid - list id
-    attachments - list of attachments (uses the hashes)
-    _raw_msg - the original message bytes (not used)
-
-    Returns: "r<hash>@<lid>" where hash is sha224 of the message items noted 
above
-    """
-    # Use text body
-    if not body:  # Make sure body is not None, which will fail.
-        body = ""
-    xbody = body if type(body) is bytes else body.encode('utf-8', 
errors='ignore')
-
-    # Crop out any trailing whitespace in body
-    xbody = re.sub(b"\s+$", b"", xbody)
-
-    # Use Message-Id (or '' if missing)
-    xbody += bytes(msg.get('message-id', ''), encoding='ascii')
-
-    # Use Date header. Don't use archived-at, as the archiver sets this if not 
present.
-    mdatestring = "(null)"  # Default to null, ONLY changed if replicable 
across imports
-    try:
-        mdate = email.utils.parsedate_tz(msg.get('date'))
-        mdatestring = time.strftime("%Y/%m/%d %H:%M:%S", 
time.gmtime(email.utils.mktime_tz(mdate)))
-    except:
-        pass
-    xbody += bytes(mdatestring, encoding='ascii')
-
-    # Use sender
-    sender = msg.get('from', None)
-    if sender:
-        xbody += bytes(sender, encoding='ascii')
-
-    # Use subject
-    subject = msg.get('subject', None)
-    if subject:
-        xbody += bytes(subject, encoding='ascii')
-
-    # Use attachment hashes if present
-    if attachments:
-        for a in attachments:
-            xbody += bytes(a['hash'], encoding='ascii')
-
-    # generate the hash and combine with the lid to form the id
-    mid = "r%s@%s" % (hashlib.sha224(xbody).hexdigest(), lid)
-    return mid
-
-
-# Old school way of making IDs
-def legacy(msg, body, lid, _attachments, _raw_msg):
-    """
-    Original generator - DO NOT USE
-    (does not generate unique ids)
-
-    The hash input is created from
-    - body: if bytes as is else encoded ascii, ignoring invalid characters; if 
the body is null an Exception is thrown
-
-    The uid_mdate for the id is the Date converted to UTC epoch else 0
-
-    Parameters:
-    msg - the parsed message (used to get the date)
-    body - the parsed text content (may be null)
-    lid - list id
-    _attachments - list of attachments (not used)
-    _raw_msg - the original message bytes (not used)
-
-    Returns: "<hash>@<uid_mdate>@<lid>" where hash is sha224 of the message 
items noted above
-    """
-    uid_mdate = 0  # Default if no date found
-    try:
-        mdate = email.utils.parsedate_tz(msg.get('date'))
-        uid_mdate = email.utils.mktime_tz(mdate)  # Only set if Date header is 
valid
-    except:
-        pass
-    mid = "%s@%s@%s" % (
-    hashlib.sha224(body if type(body) is bytes else body.encode('utf-8', 
'ignore')).hexdigest(), uid_mdate, lid)
-    return mid
-
-
 __GENERATORS = {
     'dkim': dkim,
     'full': full,
-    'medium': medium,
-    'cluster': cluster,
-    'legacy': legacy,
+    'medium': plugins.generators_old.medium,
+    'cluster': plugins.generators_old.cluster,
+    'legacy': plugins.generators_old.legacy,
 }
 
 
diff --git a/tools/plugins/generators.py b/tools/plugins/generators_old.py
similarity index 55%
copy from tools/plugins/generators.py
copy to tools/plugins/generators_old.py
index 79ae9c9..21ebd1d 100644
--- a/tools/plugins/generators.py
+++ b/tools/plugins/generators_old.py
@@ -16,163 +16,13 @@
 # limitations under the License.
 
 """
-This file contains the various ID generators for Pony Mail's archivers.
+This file contains the various older ID generators for Pony Mail's archivers.
 """
 
-import base64
 import hashlib
 import email.utils
 import time
 import re
-import typing
-
-# For optional nonce
-config: typing.Optional[dict] = None
-
-# Headers from RFC 4871, the precursor to RFC 6376
-rfc4871_subset = {
-    b"from", b"sender", b"reply-to", b"subject", b"date",
-    b"message-id", b"to", b"cc", b"mime-version", b"content-type",
-    b"content-transfer-encoding", b"content-id",
-    b"content-description", b"resent-date", b"resent-from",
-    b"resent-sender", b"resent-to", b"resent-cc",
-    b"resent-message-id", b"in-reply-to", b"references", b"list-id",
-    b"list-help", b"list-unsubscribe", b"list-subscribe",
-    b"list-post", b"list-owner", b"list-archive", b"dkim-signature"
-}
-
-# Authenticity headers from RFC 8617
-rfc4871_and_rfc8617_subset = rfc4871_subset | {
-    b"arc-authentication-results", b"arc-message-signature",
-    b"arc-seal"
-}
-
-
-def rfc822_parse_dkim(suffix,
-                      head_canon=False, body_canon=False,
-                      head_subset=None, archive_list_id=None):
-    headers = []
-    keep = True
-    list_ids = set()
-
-    while suffix:
-        # Edge case: headers don't end LF (add LF)
-        line, suffix = (suffix.split(b"\n", 1) + [b""])[:2]
-        if line in {b"\r", b""}:
-            break
-        end = b"\n" if line.endswith(b"\r") else b"\r\n"
-        if line[0] in {0x09, 0x20}:
-            # Edge case: starts with a continuation (treat like From)
-            if headers and (keep is True):
-                headers[-1][1] += line + end
-        elif not line.startswith(b"From "):
-            # Edge case: header start contains no colon (use whole line)
-            # "A field-name MUST be contained on one line." (RFC 822 B.2)
-            k, v = (line.split(b":", 1) + [b""])[:2]
-            k_lower = k.lower()
-            if k_lower == "list-id":
-                list_ids.add(k_lower)
-            if (head_subset is None) or (k_lower in head_subset):
-                keep = True
-                headers.append([k, v + end])
-            else:
-                keep = False
-    # The remaining suffix is the body
-    body = suffix.replace(b"\r\n", b"\n")
-    body = body.replace(b"\n", b"\r\n")
-
-    # Optional X-Archive-List-ID augmentation
-    if (archive_list_id is not None) and (archive_list_id not in list_ids):
-        xali_value = b" " + bytes(archive_list_id, "ascii")
-        headers.append([b"X-Archive-List-ID", xali_value])
-    # Optional nonce from local config
-    if config is not None:
-        if (config.get("archiver") and
-                config['archiver'].get('nonce')):
-            nonce = config['archiver'].get('nonce')
-            headers.append([b"X-Archive-Nonce", nonce])
-    # Optional head canonicalisation (DKIM relaxed)
-    if head_canon is True:
-        for i in range(len(headers)):
-            k, v = headers[i]
-            crlf = v.endswith(b"\r\n")
-            if crlf is True:
-                v = v[:-2]
-            v = v.replace(b"\r\n", b"")
-            v = v.replace(b"\t", b" ")
-            v = v.strip(b" ")
-            v = b" ".join(vv for vv in v.split(b" ") if vv)
-            if crlf is True:
-                v = v + b"\r\n"
-            headers[i] = [k.lower(), v]
-    # Optional body canonicalisation (DKIM simple)
-    if body_canon is True:
-        while body.endswith(b"\r\n\r\n"):
-            body = body[:-2]
-    return (headers, body)
-
-
-def pibble(hashable, size=10):
-    table = bytes.maketrans(
-        b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
-        b"0123456789bcdfghjklmnopqrstvwxyz",
-    )
-    digest = hashlib.sha3_256(hashable).digest()
-    prefix = digest[:size]
-    encoded = base64.b32encode(prefix)
-    return str(encoded.translate(table), "ascii")
-
-
-# DKIM generator: uses DKIM canonicalisation
-# Used by default
-def dkim(_msg, _body, lid, _attachments, raw_msg):
-    """
-    DKIM generator: uses DKIM relaxed/simple canonicalisation
-    We use the headers recommended in RFC 4871, plus DKIM-Signature
-
-    Parameters:
-    _msg - the parsed message (not used)
-    _body - the parsed text content (not used)
-    lid - list id
-    _attachments - list of attachments (not used)
-    raw_msg - the original message bytes
-
-    Returns: str "<pibble>", a sixteen char custom base32 encoded hash
-    """
-    headers, body = rfc822_parse_dkim(raw_msg,
-                                      head_canon=True, body_canon=True,
-                                      head_subset=rfc4871_subset, 
archive_list_id=lid)
-    hashable = b"".join([h for header in headers for h in header])
-    if body:
-        hashable += b"\r\n" + body
-    # The pibble is the 80-bit SHA3-256 prefix
-    # It is base32 encoded using 0-9 a-z except [aeiu]
-    return pibble(hashable)
-
-
-# Full generator: uses the entire email (including server-dependent data)
-# Used by default until August 2020.
-# See 'dkim' for recommended generation.
-def full(msg, _body, lid, _attachments, _raw_msg):
-    """
-    Full generator: uses the entire email
-    (including server-dependent data)
-    The id is almost certainly unique,
-    but different copies of the message are likely to have different headers, 
thus ids
-    WARNING: the archiver by default adds an archived-at header with the 
current time.
-    This is included in the hash, so messages will get different Permalinks if 
reloaded from source
-
-    Parameters:
-    msg - the parsed message
-    _body - the parsed text content (not used)
-    lid - list id
-    _attachments - list of attachments (not used)
-    _raw_msg - the original message bytes (not used)
-
-    Returns: "<hash>@<lid>" where hash is sha224 of message bytes
-    """
-    mid = "%s@%s" % (hashlib.sha224(msg.as_bytes()).hexdigest(), lid)
-    return mid
 
 
 # Medium: Standard 0.9 generator - Not recommended for future installations.
@@ -336,28 +186,3 @@ def legacy(msg, body, lid, _attachments, _raw_msg):
     mid = "%s@%s@%s" % (
     hashlib.sha224(body if type(body) is bytes else body.encode('utf-8', 
'ignore')).hexdigest(), uid_mdate, lid)
     return mid
-
-
-__GENERATORS = {
-    'dkim': dkim,
-    'full': full,
-    'medium': medium,
-    'cluster': cluster,
-    'legacy': legacy,
-}
-
-
-def generator(name):
-    try:
-        return __GENERATORS[name]
-    except KeyError:
-        print("WARN: generator %s not found, defaulting to 'legacy'" % name)
-        return legacy
-
-
-def generate(name, msg, body, lid, attachments, raw_msg):
-    return generator(name)(msg, body, lid, attachments, raw_msg)
-
-
-def generator_names():
-    return list(__GENERATORS)

Reply via email to