diff --git a/pymodmilter/__init__.py b/pymodmilter/__init__.py index 38c78ba..bbc3e08 100644 --- a/pymodmilter/__init__.py +++ b/pymodmilter/__init__.py @@ -199,6 +199,33 @@ class ModifyMilter(Milter.Base): self.logger.debug( f"accepted milter connection from {self.IP} " f"port {self.port}") + + # pre-filter rules and actions by the host condition + # also check if the mail body is needed by any upcoming action. + self.rules = [] + self._headersonly = True + for rule in ModifyMilter._rules: + if rule.conditions is None or \ + rule.conditions.match_host(self.IP): + actions = [] + for action in rule.actions: + if action.conditions is None or \ + action.conditions.match_host(self.IP): + actions.append(action) + if not action.headersonly(): + self._headersonly = False + + if actions: + # copy needed rules to preserve configured actions + rule = copy(rule) + rule.actions = actions + self.rules.append(rule) + + if not self.rules: + self.logger.debug( + "host is ignored by all rules, skip further processing") + return Milter.ACCEPT + except Exception as e: self.logger.exception( f"an exception occured in connect method: {e}") @@ -244,37 +271,6 @@ class ModifyMilter(Milter.Base): self.logger = CustomLogger( self.logger, {"qid": self.qid, "name": "milter"}) self.logger.debug("received queue-id from MTA") - - # pre-filter rules and actions by the host condition, other - # conditions (headers, envelope-from, envelope-to) may get - # changed by executed actions later on. - # also check if the mail body is needed by any upcoming action. - - self.rules = [] - self._headersonly = True - for rule in ModifyMilter._rules: - if rule.conditions is None or \ - rule.conditions.match(host=self.IP, qid=self.qid): - actions = [] - for action in rule.actions: - if action.conditions is None or \ - action.conditions.match(host=self.IP, - qid=self.qid): - actions.append(action) - if not action.headersonly(): - self._headersonly = False - - if actions: - # copy needed rules to preserve configured actions - rule = copy(rule) - rule.actions = actions - self.rules.append(rule) - - if not self.rules: - self.logger.debug( - "message is ignored by all rules, skip further processing") - return Milter.ACCEPT - self.fp = BytesIO() except Exception as e: self.logger.exception( @@ -341,7 +337,7 @@ class ModifyMilter(Milter.Base): refold_source='none')) self.msginfo = { "mailfrom": self.mailfrom, - "rcpts": self.rcpts, + "rcpts": [*self.rcpts], "vars": {}} self._replacebody = False @@ -356,7 +352,7 @@ class ModifyMilter(Milter.Base): if self._replacebody: data = self.msg.as_bytes() body_pos = data.find(b"\r\n\r\n") + 4 - self.logger.debug("replacebody") + self.logger.debug("replace body") super().replacebody(data[body_pos:]) del data diff --git a/pymodmilter/action.py b/pymodmilter/action.py index ecbd880..7287478 100644 --- a/pymodmilter/action.py +++ b/pymodmilter/action.py @@ -194,9 +194,6 @@ class Action: logger = CustomLogger( self.logger, {"qid": milter.qid, "name": self._name}) if self.conditions is None or \ - self.conditions.match(envfrom=milter.mailfrom, - envto=[*milter.rcpts], - headers=milter.msg.items(), - qid=milter.qid): + self.conditions.match(milter): return self._class.execute( milter=milter, pretend=self.pretend, logger=logger) diff --git a/pymodmilter/conditions.py b/pymodmilter/conditions.py index 99bad32..fb438a0 100644 --- a/pymodmilter/conditions.py +++ b/pymodmilter/conditions.py @@ -81,49 +81,51 @@ class Conditions: self._args = cfg["args"] self.logger = cfg.logger - def match(self, host=None, envfrom=None, envto=None, headers=None, - qid=None): - if qid is None: - logger = self.logger - else: - logger = CustomLogger( - self.logger, {"qid": qid, "name": self._name}) + def match_host(self, host): + logger = CustomLogger( + self.logger, {"name": self._name}) - if host: - ip = IPAddress(host) + ip = IPAddress(host) - if "local" in self._args: - is_local = False - for addr in self._local_addrs: - if ip in addr: - is_local = True - break - - if is_local != self._args["local"]: - logger.debug( - f"ignore host {host}, " - f"condition local does not match") - return False + if "local" in self._args: + is_local = False + for addr in self._local_addrs: + if ip in addr: + is_local = True + break + if is_local != self._args["local"]: logger.debug( - f"condition local matches for host {host}") + f"ignore host {host}, " + f"condition local does not match") + return False - if "hosts" in self._args: - found = False - for addr in self._args["hosts"]: - if ip in addr: - found = True - break + logger.debug( + f"condition local matches for host {host}") - if not found: - logger.debug( - f"ignore host {host}, " - f"condition hosts does not match") - return False + if "hosts" in self._args: + found = False + for addr in self._args["hosts"]: + if ip in addr: + found = True + break + if not found: logger.debug( - f"condition hosts matches for host {host}") + f"ignore host {host}, " + f"condition hosts does not match") + return False + logger.debug( + f"condition hosts matches for host {host}") + + return True + + def match(self, milter): + logger = CustomLogger( + self.logger, {"qid": milter.qid, "name": self._name}) + + envfrom = milter.msginfo["mailfrom"] if envfrom and "envfrom" in self._args: if not self._args["envfrom"].match(envfrom): logger.debug( @@ -135,6 +137,7 @@ class Conditions: f"condition envfrom matches for " f"envelope-from address {envfrom}") + envto = milter.msginfo["rcpts"] if envto and "envto" in self._args: if not isinstance(envto, list): envto = [envto] @@ -150,9 +153,9 @@ class Conditions: f"condition envto matches for " f"envelope-to address {envto}") - if headers and "header" in self._args: + if "header" in self._args: match = None - for field, value in headers: + for field, value in milter.msg.items(): header = f"{field}: {value}" match = self._args["header"].search(header) if match: diff --git a/pymodmilter/rule.py b/pymodmilter/rule.py index fae266d..70cfe3b 100644 --- a/pymodmilter/rule.py +++ b/pymodmilter/rule.py @@ -86,10 +86,7 @@ class Rule: def execute(self, milter): """Execute all actions of this rule.""" if self.conditions is None or \ - self.conditions.match(envfrom=milter.mailfrom, - envto=[*milter.rcpts], - headers=milter.msg.items(), - qid=milter.qid): + self.conditions.match(milter): for action in self.actions: milter_action = action.execute(milter) if milter_action is not None: