Switch back to email.header lib because of error handling
This commit is contained in:
@@ -23,12 +23,33 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from Milter.utils import parse_addr
|
from Milter.utils import parse_addr
|
||||||
from email.message import EmailMessage
|
from email.charset import Charset
|
||||||
from email.parser import HeaderParser
|
from email.header import Header, decode_header
|
||||||
from email.policy import default as default_policy
|
|
||||||
from netaddr import IPAddress, IPNetwork, AddrFormatError
|
from netaddr import IPAddress, IPNetwork, AddrFormatError
|
||||||
|
|
||||||
|
|
||||||
|
def make_header(decoded_seq, maxlinelen=None, header_name=None,
|
||||||
|
continuation_ws=' ', errors='strict'):
|
||||||
|
"""Create a Header from a sequence of pairs as returned by decode_header()
|
||||||
|
|
||||||
|
decode_header() takes a header value string and returns a sequence of
|
||||||
|
pairs of the format (decoded_string, charset) where charset is the string
|
||||||
|
name of the character set.
|
||||||
|
|
||||||
|
This function takes one of those sequence of pairs and returns a Header
|
||||||
|
instance. Optional maxlinelen, header_name, and continuation_ws are as in
|
||||||
|
the Header constructor.
|
||||||
|
"""
|
||||||
|
h = Header(maxlinelen=maxlinelen, header_name=header_name,
|
||||||
|
continuation_ws=continuation_ws)
|
||||||
|
for s, charset in decoded_seq:
|
||||||
|
# None means us-ascii but we can simply pass it on to h.append()
|
||||||
|
if charset is not None and not isinstance(charset, Charset):
|
||||||
|
charset = Charset(charset)
|
||||||
|
h.append(s, charset, errors=errors)
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
class HeaderRule:
|
class HeaderRule:
|
||||||
"""HeaderRule to implement a rule to apply on e-mail headers."""
|
"""HeaderRule to implement a rule to apply on e-mail headers."""
|
||||||
|
|
||||||
@@ -145,19 +166,19 @@ class HeaderRule:
|
|||||||
else:
|
else:
|
||||||
occurrences[name] += 1
|
occurrences[name] += 1
|
||||||
|
|
||||||
value = header[name]
|
|
||||||
# check if header line matches regex
|
# check if header line matches regex
|
||||||
if self.header.search(f"{name}: {value}"):
|
header_line = str(header)
|
||||||
|
if self.header.search(header_line):
|
||||||
|
value = header_line.split(":", 1)[1].strip()
|
||||||
if self.action == "del":
|
if self.action == "del":
|
||||||
# set an empty value to delete the header
|
# set an empty value to delete the header
|
||||||
new_value = ""
|
new_value = ""
|
||||||
else:
|
else:
|
||||||
new_value = self.search.sub(self.value, value)
|
new_value = self.search.sub(self.value, value)
|
||||||
if value != new_value:
|
if value != new_value:
|
||||||
header = EmailMessage(policy=default_policy)
|
header = make_header(
|
||||||
# Remove line breaks, EmailMessage object
|
decode_header(
|
||||||
#does not like them
|
f"{name}: {new_value}"), errors="replace")
|
||||||
header.add_header(name, " ".join(new_value.splitlines()))
|
|
||||||
modified.append((name, header, index, occurrences[name]))
|
modified.append((name, header, index, occurrences[name]))
|
||||||
index += 1
|
index += 1
|
||||||
return modified
|
return modified
|
||||||
@@ -215,15 +236,18 @@ class HeaderMilter(Milter.Base):
|
|||||||
self.headers = []
|
self.headers = []
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@Milter.noreply
|
|
||||||
def header(self, name, value):
|
def header(self, name, value):
|
||||||
|
try:
|
||||||
# remove surrogates from value
|
# remove surrogates from value
|
||||||
value = value.encode(errors="surrogateescape").decode(errors="replace")
|
value = value.encode(errors="surrogateescape").decode(errors="replace")
|
||||||
self.logger.debug(f"{self.qid}: received header: {name}: {value}")
|
self.logger.debug(f"{self.qid}: received header: {name}: {value}")
|
||||||
header = HeaderParser(
|
header = make_header(decode_header(f"{name}: {value}"), errors="replace")
|
||||||
policy=default_policy).parsestr(f"{name}: {value}")
|
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"{self.qid}: decoded header: {name}: {header[name]}")
|
f"{self.qid}: decoded header: {header}")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.exception(
|
||||||
|
f"an exception occured in header function: {e}")
|
||||||
|
return Milter.TEMPFAIL
|
||||||
self.headers.append((name, header))
|
self.headers.append((name, header))
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@@ -232,51 +256,45 @@ class HeaderMilter(Milter.Base):
|
|||||||
for rule in self.rules:
|
for rule in self.rules:
|
||||||
self.logger.debug(f"{self.qid}: executing rule '{rule.name}'")
|
self.logger.debug(f"{self.qid}: executing rule '{rule.name}'")
|
||||||
modified = rule.execute(self.headers)
|
modified = rule.execute(self.headers)
|
||||||
|
|
||||||
for name, header, index, occurrence in modified:
|
for name, header, index, occurrence in modified:
|
||||||
value = header[name]
|
header_line = str(header)
|
||||||
# remove illegal characters, pymilter does not like them
|
value = header.encode().split(":", 1)[1].strip()
|
||||||
enc_value = header.as_string().replace(
|
|
||||||
"\r", "").replace(
|
|
||||||
"\n", "").replace(
|
|
||||||
"\x00", "").split(
|
|
||||||
":", 1)[1].strip()
|
|
||||||
mod_header = f"{name}: {value}"
|
|
||||||
if rule.action == "add":
|
if rule.action == "add":
|
||||||
if rule.log:
|
if rule.log:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"{self.qid}: add: header: {mod_header[0:70]}")
|
f"{self.qid}: add: header: "
|
||||||
|
f"{header_line[0:70]}")
|
||||||
else:
|
else:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"{self.qid}: add: header: {mod_header}")
|
f"{self.qid}: add: header: "
|
||||||
|
f"{header_line}")
|
||||||
self.headers.insert(0, (name, header))
|
self.headers.insert(0, (name, header))
|
||||||
self.addheader(name, enc_value, 1)
|
self.addheader(name, value, 1)
|
||||||
else:
|
else:
|
||||||
if rule.action == "mod":
|
if rule.action == "mod":
|
||||||
old_value = self.headers[index][1][name]
|
old_header = str(self.headers[index][1])
|
||||||
old_header = f"{name}: {old_value}"
|
|
||||||
if rule.log:
|
if rule.log:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"{self.qid}: modify: header: "
|
f"{self.qid}: modify: header: "
|
||||||
f"{old_header[0:70]}: {mod_header[0:70]}")
|
f"{old_header[0:70]}: {header_line[0:70]}")
|
||||||
else:
|
else:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"{self.qid}: modify: header "
|
f"{self.qid}: modify: header "
|
||||||
f"(occ. {occurrence}): {old_header}: "
|
f"(occ. {occurrence}): {old_header}: "
|
||||||
f"{mod_header}")
|
f"{header_line}")
|
||||||
self.headers[index] = (name, header)
|
self.headers[index] = (name, header)
|
||||||
elif rule.action == "del":
|
elif rule.action == "del":
|
||||||
if rule.log:
|
if rule.log:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"{self.qid}: delete: header: "
|
f"{self.qid}: delete: header: "
|
||||||
f"{mod_header[0:70]}")
|
f"{header_line[0:70]}")
|
||||||
else:
|
else:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"{self.qid}: delete: header "
|
f"{self.qid}: delete: header "
|
||||||
f"(occ. {occurrence}): {mod_header}")
|
f"(occ. {occurrence}): {header_line}")
|
||||||
del self.headers[index]
|
del self.headers[index]
|
||||||
|
|
||||||
self.chgheader(name, occurrence, enc_value)
|
self.chgheader(name, occurrence, value)
|
||||||
return Milter.ACCEPT
|
return Milter.ACCEPT
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception(
|
self.logger.exception(
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ def read_file(fname):
|
|||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
setup(name = "pyheadermilter",
|
setup(name = "pyheadermilter",
|
||||||
version = "0.0.6",
|
version = "0.0.7",
|
||||||
author = "Thomas Oettli",
|
author = "Thomas Oettli",
|
||||||
author_email = "spacefreak@noop.ch",
|
author_email = "spacefreak@noop.ch",
|
||||||
description = "A pymilter based sendmail/postfix pre-queue filter.",
|
description = "A pymilter based sendmail/postfix pre-queue filter.",
|
||||||
|
|||||||
Reference in New Issue
Block a user