Decode headers before processing
This commit is contained in:
@@ -25,6 +25,8 @@ 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.policy import default as default_policy
|
||||||
from netaddr import IPAddress, IPNetwork, AddrFormatError
|
from netaddr import IPAddress, IPNetwork, AddrFormatError
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ class HeaderRule:
|
|||||||
def __init__(self, name, action, header, search="", value="", ignore_hosts=[], ignore_envfrom=None, only_hosts=[], log=True):
|
def __init__(self, name, action, header, search="", value="", ignore_hosts=[], ignore_envfrom=None, only_hosts=[], log=True):
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.name = name
|
self.name = name
|
||||||
self._action = action
|
self.action = action
|
||||||
self.header = header
|
self.header = header
|
||||||
self.search = search
|
self.search = search
|
||||||
self.value = value
|
self.value = value
|
||||||
@@ -79,12 +81,6 @@ class HeaderRule:
|
|||||||
except AddrFormatError as e:
|
except AddrFormatError as e:
|
||||||
raise RuntimeError("unable to parse option 'only_hosts' of rule '{}': {}".format(name, e))
|
raise RuntimeError("unable to parse option 'only_hosts' of rule '{}': {}".format(name, e))
|
||||||
|
|
||||||
def get_action(self):
|
|
||||||
return self._action
|
|
||||||
|
|
||||||
def log_modification(self):
|
|
||||||
return self.log
|
|
||||||
|
|
||||||
def ignore_host(self, host):
|
def ignore_host(self, host):
|
||||||
ip = IPAddress(host)
|
ip = IPAddress(host)
|
||||||
ignore = False
|
ignore = False
|
||||||
@@ -118,7 +114,7 @@ class HeaderRule:
|
|||||||
|
|
||||||
def execute(self, headers):
|
def execute(self, headers):
|
||||||
"""Execute rule on given headers and return list with modified headers."""
|
"""Execute rule on given headers and return list with modified headers."""
|
||||||
if self._action == "add":
|
if self.action == "add":
|
||||||
return [(self.header, self.value, 0, 1)]
|
return [(self.header, self.value, 0, 1)]
|
||||||
|
|
||||||
modified = []
|
modified = []
|
||||||
@@ -126,7 +122,7 @@ class HeaderRule:
|
|||||||
occurrences = {}
|
occurrences = {}
|
||||||
|
|
||||||
# iterate headers
|
# iterate headers
|
||||||
for name, value in headers:
|
for name, hdr in headers:
|
||||||
# keep track of the occurrence of each header, needed by Milter.Base.chgheader
|
# keep track of the occurrence of each header, needed by Milter.Base.chgheader
|
||||||
if name not in occurrences.keys():
|
if name not in occurrences.keys():
|
||||||
occurrences[name] = 1
|
occurrences[name] = 1
|
||||||
@@ -134,14 +130,18 @@ class HeaderRule:
|
|||||||
occurrences[name] += 1
|
occurrences[name] += 1
|
||||||
|
|
||||||
# check if header line matches regex
|
# check if header line matches regex
|
||||||
|
value = hdr[name]
|
||||||
if self.header.search("{}: {}".format(name, value)):
|
if self.header.search("{}: {}".format(name, value)):
|
||||||
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:
|
||||||
|
str(hdr).split(": ", 1)[1].strip()
|
||||||
new_value = self.search.sub(self.value, value)
|
new_value = self.search.sub(self.value, value)
|
||||||
if value != new_value:
|
if value != new_value:
|
||||||
modified.append((name, new_value, index, occurrences[name]))
|
hdr = EmailMessage(policy=default_policy)
|
||||||
|
hdr[name] = new_value
|
||||||
|
modified.append((name, hdr, index, occurrences[name]))
|
||||||
index += 1
|
index += 1
|
||||||
return modified
|
return modified
|
||||||
|
|
||||||
@@ -194,7 +194,11 @@ class HeaderMilter(Milter.Base):
|
|||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def header(self, name, value):
|
def header(self, name, value):
|
||||||
self.headers.append((name, value.encode(errors="surrogateescape").decode(errors="replace")))
|
# remove surrogates from value
|
||||||
|
value = value.encode(errors="surrogateescape").decode(errors="replace")
|
||||||
|
hdr = EmailMessage(policy=default_policy)
|
||||||
|
hdr[name] = value
|
||||||
|
self.headers.append((name, hdr))
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def eom(self):
|
def eom(self):
|
||||||
@@ -202,36 +206,37 @@ class HeaderMilter(Milter.Base):
|
|||||||
for rule in self.rules:
|
for rule in self.rules:
|
||||||
self.logger.debug("{}: executing rule '{}'".format(self.queueid, rule.name))
|
self.logger.debug("{}: executing rule '{}'".format(self.queueid, rule.name))
|
||||||
modified = rule.execute(self.headers)
|
modified = rule.execute(self.headers)
|
||||||
action = rule.get_action()
|
|
||||||
log = rule.log_modification()
|
|
||||||
|
|
||||||
for name, value, index, occurrence in modified:
|
for name, hdr, index, occurrence in modified:
|
||||||
|
value = hdr[name]
|
||||||
|
encoded_value = bytes(header).decode().split(": ")[1].rstrip()
|
||||||
mod_header = "{}: {}".format(name, value)
|
mod_header = "{}: {}".format(name, value)
|
||||||
if action == "add":
|
if rule.action == "add":
|
||||||
if log:
|
if rule.log:
|
||||||
self.logger.info("{}: add: header: {}".format(self.queueid, mod_header[0:70]))
|
self.logger.info("{}: add: header: {}".format(self.queueid, mod_header[0:70]))
|
||||||
else:
|
else:
|
||||||
self.logger.debug("{}: add: header: {}".format(self.queueid, mod_header))
|
self.logger.debug("{}: add: header: {}".format(self.queueid, mod_header))
|
||||||
|
self.headers.insert(0, (name, hdr))
|
||||||
self.headers.insert(0, (name, value))
|
self.addheader(name, encoded_value, 1)
|
||||||
self.addheader(name, value, 1)
|
|
||||||
else:
|
else:
|
||||||
if action == "mod":
|
if rule.action == "mod":
|
||||||
old_header = "{}: {}".format(name, self.headers[index][1])
|
old_value = self.headers[index][1][name]
|
||||||
if log:
|
old_header = "{}: {}".format(name, old_value)
|
||||||
|
if rule.log:
|
||||||
self.logger.info("{}: modify: header: {}: {}".format(
|
self.logger.info("{}: modify: header: {}: {}".format(
|
||||||
self.queueid, old_header[0:70], mod_header[0:70]))
|
self.queueid, old_header[0:70], mod_header[0:70]))
|
||||||
else:
|
else:
|
||||||
self.logger.debug("{}: modify: header (occ. {}): {}: {}".format(
|
self.logger.debug("{}: modify: header (occ. {}): {}: {}".format(
|
||||||
self.queueid, occurrence, old_header, mod_header))
|
self.queueid, occurrence, old_header, mod_header))
|
||||||
self.headers[index] = (name, value)
|
self.headers[index] = (name, hdr)
|
||||||
elif action == "del":
|
elif rule.action == "del":
|
||||||
if log:
|
if rule.log:
|
||||||
self.logger.info("{}: delete: header: {}".format(self.queueid, mod_header[0:70]))
|
self.logger.info("{}: delete: header: {}".format(self.queueid, mod_header[0:70]))
|
||||||
else:
|
else:
|
||||||
self.logger.debug("{}: delete: header (occ. {}): {}".format(self.queueid, occurrence, mod_header))
|
self.logger.debug("{}: delete: header (occ. {}): {}".format(self.queueid, occurrence, mod_header))
|
||||||
del self.headers[index]
|
del self.headers[index]
|
||||||
self.chgheader(name, occurrence, value)
|
|
||||||
|
self.chgheader(name, occurrence, encoded_value)
|
||||||
return Milter.ACCEPT
|
return Milter.ACCEPT
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception("an exception occured in eom function: {}".format(e))
|
self.logger.exception("an exception occured in eom function: {}".format(e))
|
||||||
|
|||||||
Reference in New Issue
Block a user