change header handling in add_disclaimer and store

This commit is contained in:
2020-11-10 19:55:18 +01:00
parent 4269420633
commit 1e228c91ba
3 changed files with 28 additions and 35 deletions

View File

@@ -220,8 +220,8 @@ class ModifyMilter(Milter.Base):
if not self.rules: if not self.rules:
self.logger.debug( self.logger.debug(
f"envelope-to addresses are ignored by all rules, " "envelope-to addresses are ignored by all rules, "
f"skip further processing") "skip further processing")
return Milter.ACCEPT return Milter.ACCEPT
self.qid = self.getsymval('i') self.qid = self.getsymval('i')
@@ -229,7 +229,7 @@ class ModifyMilter(Milter.Base):
self.logger.debug("received queue-id from MTA") self.logger.debug("received queue-id from MTA")
self.fields = None self.fields = None
self.fields_data = None self.fields_bytes = None
self.body_data = None self.body_data = None
needs = [] needs = []
for rule in self.rules: for rule in self.rules:
@@ -238,8 +238,8 @@ class ModifyMilter(Milter.Base):
if "fields" in needs: if "fields" in needs:
self.fields = [] self.fields = []
if "original_fields" in needs: if "fields_bytes" in needs:
self.fields_data = BytesIO() self.fields_bytes = []
if "body" in needs: if "body" in needs:
self.body_data = BytesIO() self.body_data = BytesIO()
@@ -253,13 +253,10 @@ class ModifyMilter(Milter.Base):
def header(self, name, value): def header(self, name, value):
try: try:
if self.fields_data != None: if self.fields_data is not None:
self.fields_data.write( self.fields_bytes.append(
name.encode("ascii", errors="surrogateescape")) (name.encode("ascii", errors="surrogateescape"),
self.fields_data.write(b": ") value.encode("ascii", errors="surrogateescape")))
self.fields_data.write(
value.encode("ascii", errors="surrogateescape"))
self.fields_data.write(b"\r\n")
if self.fields is not None: if self.fields is not None:
# remove surrogates from value # remove surrogates from value

View File

@@ -87,8 +87,8 @@ def mod_header(field, value, milter, search=None, pretend=False,
if not new_v: if not new_v:
logger.warning( logger.warning(
f"mod_header: resulting value is empty, " "mod_header: resulting value is empty, "
f"skip modification") "skip modification")
continue continue
header = f"{f}: {v}" header = f"{f}: {v}"
@@ -279,19 +279,15 @@ def add_disclaimer(text, html, action, policy, milter, pretend=False,
milter.body_data.seek(0) milter.body_data.seek(0)
fp = BytesFeedParser(policy=default_policy) fp = BytesFeedParser(policy=default_policy)
for field, value in milter.fields: for field, value in milter.fields_bytes:
field_lower = field.lower() field_lower = field.encode("ascii").lower()
if not field_lower.startswith("content-") and \ if not field_lower.startswith("content-") and \
field_lower != "mime-version": field_lower != "mime-version":
continue continue
logger.debug( logger.debug(
f"feed content header to message object: {field}: {value}") f"feed content header to message object: "
encoded_value = _replace_illegal_chars( f"{field.encode()}: {value.encode()}")
Header(s=value).encode()) fp.feed(field + b": " + value + b"\r\n")
fp.feed(field.encode("ascii", errors="replace"))
fp.feed(b": ")
fp.feed(encoded_value.encode("ascii", errors="replace"))
fp.feed(b"\r\n")
fp.feed(b"\r\n") fp.feed(b"\r\n")
logger.debug(f"feed body to message object: {field}: {value}") logger.debug(f"feed body to message object: {field}: {value}")
@@ -318,13 +314,13 @@ def add_disclaimer(text, html, action, policy, milter, pretend=False,
logger.warning(e) logger.warning(e)
if policy == "ignore": if policy == "ignore":
logger.info( logger.info(
f"unable to add disclaimer to message body, " "unable to add disclaimer to message body, "
f"ignore error according to policy") "ignore error according to policy")
return return
elif policy == "reject": elif policy == "reject":
logger.info( logger.info(
f"unable to add disclaimer to message body, " "unable to add disclaimer to message body, "
f"reject message according to policy") "reject message according to policy")
return [ return [
("reject", "Message rejected due to error")] ("reject", "Message rejected due to error")]
@@ -391,13 +387,13 @@ def store(directory, milter, pretend=False,
timestamp = datetime.now().strftime("%Y%m%d%H%M%S") timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
store_id = f"{timestamp}_{milter.qid}" store_id = f"{timestamp}_{milter.qid}"
datafile = os.path.join(directory, store_id) datafile = os.path.join(directory, store_id)
milter.fields_data.seek(0)
milter.body_data.seek(0) milter.body_data.seek(0)
logger.info(f"store message in file {datafile}") logger.info(f"store message in file {datafile}")
try: try:
with open(datafile, "wb") as fp: with open(datafile, "wb") as fp:
copyfileobj(milter.fields_data, fp) for field, value in milter.fields_bytes:
fp.write(b"\r\n") fp.write(field + b": " + value + b"\r\n")
copyfileobj(milter.body_data, fp) copyfileobj(milter.body_data, fp)
except IOError as e: except IOError as e:
raise RuntimeError(f"unable to store message: {e}") raise RuntimeError(f"unable to store message: {e}")
@@ -410,7 +406,7 @@ class Action:
"del_header": ["fields"], "del_header": ["fields"],
"mod_header": ["fields"], "mod_header": ["fields"],
"add_disclaimer": ["fields", "body"], "add_disclaimer": ["fields", "body"],
"store": ["original_fields", "body"]} "store": ["fields_bytes", "body"]}
def __init__(self, name, local_addrs, conditions, action_type, args, def __init__(self, name, local_addrs, conditions, action_type, args,
loglevel=logging.INFO, pretend=False): loglevel=logging.INFO, pretend=False):
@@ -489,7 +485,7 @@ class Action:
self._func = store self._func = store
if args["storage_type"] not in ["file"]: if args["storage_type"] not in ["file"]:
raise RuntimeError( raise RuntimeError(
f"invalid storage_type 'args['storage_type']'") "invalid storage_type 'args['storage_type']'")
if args["storage_type"] == "file": if args["storage_type"] == "file":
self._args["directory"] = args["directory"] self._args["directory"] = args["directory"]

View File

@@ -125,8 +125,8 @@ def main():
socket = config["global"]["socket"] socket = config["global"]["socket"]
else: else:
raise RuntimeError( raise RuntimeError(
f"listening socket is neither specified on the command line " "listening socket is neither specified on the command line "
f"nor in the configuration file") "nor in the configuration file")
if "local_addrs" in config["global"]: if "local_addrs" in config["global"]:
local_addrs = config["global"]["local_addrs"] local_addrs = config["global"]["local_addrs"]
@@ -140,7 +140,7 @@ def main():
if "rules" not in config: if "rules" not in config:
raise RuntimeError( raise RuntimeError(
f"mandatory config section 'rules' not found") "mandatory config section 'rules' not found")
if not config["rules"]: if not config["rules"]:
raise RuntimeError("no rules configured") raise RuntimeError("no rules configured")