diff --git a/pyquarantine/cli.py b/pyquarantine/cli.py index a96a987..fe49084 100644 --- a/pyquarantine/cli.py +++ b/pyquarantine/cli.py @@ -97,7 +97,7 @@ def list_quarantines(quarantines, args): else: qlist = [] for q in quarantines: - cfg = q["args"] + cfg = q["options"] storage_type = cfg["store"]["type"] if "notify" in cfg: diff --git a/pyquarantine/config.py b/pyquarantine/config.py index f7526f1..652ecfc 100644 --- a/pyquarantine/config.py +++ b/pyquarantine/config.py @@ -269,7 +269,7 @@ class ActionConfig(BaseConfig): JSON_SCHEMA = { "type": "object", - "required": ["name", "type", "args"], + "required": ["name", "type", "options"], "additionalProperties": False, "properties": { "name": {"type": "string"}, @@ -277,14 +277,14 @@ class ActionConfig(BaseConfig): "pretend": {"type": "boolean", "default": False}, "conditions": {"type": "object"}, "type": {"enum": list(ACTION_TYPES.keys())}, - "args": {"type": "object"}}} + "options": {"type": "object"}}} def __init__(self, config, rec=True): super().__init__(config) if rec: if "conditions" in self: self["conditions"] = ConditionsConfig(self["conditions"]) - self["action"] = self.ACTION_TYPES[self["type"]](self["args"]) + self["action"] = self.ACTION_TYPES[self["type"]](self["options"]) class RuleConfig(BaseConfig): diff --git a/pyquarantine/misc/pyquarantine.conf.example b/pyquarantine/misc/pyquarantine.conf.example index c062f75..65df9c3 100644 --- a/pyquarantine/misc/pyquarantine.conf.example +++ b/pyquarantine/misc/pyquarantine.conf.example @@ -60,7 +60,7 @@ # Section: conditions # Notes: Optional conditions to process the rule. # If multiple conditions are set, they all - # have to be true to process the rule. + # have to be true for the rule to be processed. # "conditions": { # Option: local diff --git a/pyquarantine/misc/pyquarantine.conf.example_old b/pyquarantine/misc/pyquarantine.conf.example_old new file mode 100644 index 0000000..c062f75 --- /dev/null +++ b/pyquarantine/misc/pyquarantine.conf.example_old @@ -0,0 +1,221 @@ +# This is an example /etc/pyquarantine/pyquarantine.conf file. +# Copy it into place before use. +# +# The file is in JSON format. +# +# The global option 'log' can be overriden per rule or per modification. +# +{ + # Section: global + # Notes: Global options. + # + "global": { + # Option: socket + # Type: String + # Notes: The socket used to communicate with the MTA. + # + # Examples: + # unix:/path/to/socket a named pipe + # inet:8899 listen on ANY interface + # inet:8899@localhost listen on a specific interface + # inet6:8899 listen on ANY interface + # inet6:8899@[2001:db8:1234::1] listen on a specific interface + # Value: [ SOCKET ] + "socket": "inet:8898@127.0.0.1", + + # Option: local_addrs + # Type: List + # Notes: A list of local hosts and networks. + # Value: [ LIST ] + # + "local_addrs": ["fe80::/64", "::1/128", "127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], + + # Option: loglevel + # Type: String + # Notes: Set loglevel for rules and actions. + # Value: [ error | warning | info | debug ] + # + "loglevel": "info", + + # Option: pretend + # Type: Bool + # Notes: Just pretend to do the actions, for test purposes. + # Value: [ true | false ] + # + "pretend": true + }, + + # Section: rules + # Notes: Rules and related actions. + # + "rules": [ + { + # Option: name + # Type: String + # Notes: Name of the rule. + # Value: [ NAME ] + # + "name": "myrule", + + # Section: conditions + # Notes: Optional conditions to process the rule. + # If multiple conditions are set, they all + # have to be true to process the rule. + # + "conditions": { + # Option: local + # Type: Bool + # Notes: Condition wheter the senders host address is listed in local_addrs. + # Value: [ true | false ] + # + "local": false, + + # Option: hosts + # Type: String + # Notes: Condition wheter the senders host address is listed in this list. + # Value: [ LIST ] + # + "hosts": [ "127.0.0.1" ], + + # Option: envfrom + # Type: String + # Notes: Condition wheter the envelop-from address matches this regular expression. + # Value: [ REGEX ] + # + "envfrom": "^.+@mypartner\\.com$", + + # Option: envto + # Type: String + # Notes: Condition wheter the envelop-to address matches this regular expression. + # Value: [ REGEX ] + # + "envto": "^postmaster@.+$" + }, + + # Section: actions + # Notes: Actions of the rule. + # + "actions": [ + { + # Option: name + # Type: String + # Notes: Name of the modification. + # Value: [ NAME ] + # + "name": "add_test_header", + + # Option: type + # Type: String + # Notes: Type of the modification. + # Value: [ add_header | del_header | mod_header ] + # + "type": "add_header", + + # Option: field + # Type: String + # Notes: Name of the header. + # Value: [ NAME ] + # + "field": "X-Test-Header", + + # Option: value + # Type: String + # Notes: Value of the header. + # Value: [ VALUE ] + # + "value": "true" + }, { + "name": "modify_subject", + + "type": "mod_header", + + # Option: field + # Type: String + # Notes: Regular expression to match against header lines (e.g. Subject: Test-Subject). + # Value: [ REGEX ] + # + "field": "^Subject$", + + # Option: search + # Type: String + # Notes: Regular expression to match against the headers value. + # Values: [ VALUE ] + # + "search": "(?P.*)", + + # Option: value + # Type: String + # Notes: New value of the header. + # Values: [ VALUE ] + "value": "[EXTERNAL] \\g" + }, { + "name": "delete_received_header", + + "type": "del_header", + + # Option: field + # Type: String + # Notes: Regular expression to match against header lines (e.g. Subject: Test-Subject). + # Value: [ REGEX ] + # + "field": "^Received$" + }, { + "name": "add_disclaimer", + + "type": "add_disclaimer", + + # Option: action + # Type: String + # Notes: Action to perform with the disclaimer. + # Value: [ append | prepend ] + # + "action": "prepend", + + # Option: html_template + # Type: String + # Notes: Path to a file which contains the html representation of the disclaimer. + # Value: [ FILE_PATH ] + # + "html_template": "/etc/pyquarantine/templates/disclaimer_html.template", + + # Option: text_template + # Type: String + # Notes: Path to a file which contains the text representation of the disclaimer. + # Value: [ FILE_PATH ] + # + "text_template": "/etc/pyquarantine/templates/disclaimer_text.template", + + # Option: error_policy + # Type: String + # Notes: Set what should be done if the modification fails (e.g. no message body present). + # Value: [ wrap | ignore | reject ] + # + "error_policy": "wrap" + }, { + "name": "store_message", + + "type": "store", + + # Option: storage_type + # Type: String + # Notes: The storage type used to store e-mails. + # Value: [ file ] + "storage_type": "file", + + # Option: directory + # Type: String + # Notes: Directory used to store e-mails. + # Value: [ file ] + "directory": "/mnt/messages", + + # Option: original + # Type: Bool + # Notes: If set to true, store the message as received by the MTA instead of storing the current state + # of the message, that may was modified already by other actions. + # Value: [ true | false ] + "original": true + } + ] + } + ] +} diff --git a/pyquarantine/modify.py b/pyquarantine/modify.py index 1be04e8..c04a3a8 100644 --- a/pyquarantine/modify.py +++ b/pyquarantine/modify.py @@ -390,14 +390,14 @@ class Modify: self.cfg = cfg self.logger = logging.getLogger(cfg["name"]) self.logger.setLevel(cfg.get_loglevel(debug)) - cfg["args"]["pretend"] = cfg["pretend"] + cfg["options"]["pretend"] = cfg["pretend"] self._modification = self.MODIFICATION_TYPES[cfg["type"]]( - **cfg["args"]) + **cfg["options"]) self._headersonly = self._modification._headersonly def __str__(self): cfg = [] - for key, value in self.cfg["args"].items(): + for key, value in self.cfg["options"].items(): cfg.append(f"{key}={value}") class_name = type(self._modification).__name__ return f"{class_name}(" + ", ".join(cfg) + ")" diff --git a/pyquarantine/notify.py b/pyquarantine/notify.py index 6d924fc..b28a8ca 100644 --- a/pyquarantine/notify.py +++ b/pyquarantine/notify.py @@ -332,16 +332,16 @@ class Notify: self.logger = logging.getLogger(cfg["name"]) self.logger.setLevel(cfg.get_loglevel(debug)) - nodification_type = cfg["args"]["type"] - del cfg["args"]["type"] - cfg["args"]["pretend"] = cfg["pretend"] + nodification_type = cfg["options"]["type"] + del cfg["options"]["type"] + cfg["options"]["pretend"] = cfg["pretend"] self._notification = self.NOTIFICATION_TYPES[nodification_type]( - **cfg["args"]) + **cfg["options"]) self._headersonly = self._notification._headersonly def __str__(self): cfg = [] - for key, value in self.cfg["args"].items(): + for key, value in self.cfg["options"].items(): cfg.append(f"{key}={value}") class_name = type(self._notification).__name__ return f"{class_name}(" + ", ".join(cfg) + ")" diff --git a/pyquarantine/storage.py b/pyquarantine/storage.py index 727a92b..800a38f 100644 --- a/pyquarantine/storage.py +++ b/pyquarantine/storage.py @@ -375,16 +375,16 @@ class Store: self.logger = logging.getLogger(cfg["name"]) self.logger.setLevel(cfg.get_loglevel(debug)) - storage_type = cfg["args"]["type"] - del cfg["args"]["type"] - cfg["args"]["pretend"] = cfg["pretend"] + storage_type = cfg["options"]["type"] + del cfg["options"]["type"] + cfg["options"]["pretend"] = cfg["pretend"] self._storage = self.STORAGE_TYPES[storage_type]( - **cfg["args"]) + **cfg["options"]) self._headersonly = self._storage._headersonly def __str__(self): cfg = [] - for key, value in self.cfg["args"].items(): + for key, value in self.cfg["options"].items(): cfg.append(f"{key}={value}") class_name = type(self._storage).__name__ return f"{class_name}(" + ", ".join(cfg) + ")" @@ -412,25 +412,25 @@ class Quarantine: "loglevel": cfg["loglevel"], "pretend": cfg["pretend"], "type": "store", - "args": cfg["args"]["store"].get_config()}) + "options": cfg["options"]["store"].get_config()}) self._storage = Store(storage_cfg, local_addrs, debug) - self.smtp_host = cfg["args"]["smtp_host"] - self.smtp_port = cfg["args"]["smtp_port"] + self.smtp_host = cfg["options"]["smtp_host"] + self.smtp_port = cfg["options"]["smtp_port"] self._notification = None - if "notify" in cfg["args"]: + if "notify" in cfg["options"]: notify_cfg = ActionConfig({ "name": cfg["name"], "loglevel": cfg["loglevel"], "pretend": cfg["pretend"], "type": "notify", - "args": cfg["args"]["notify"].get_config()}) + "options": cfg["options"]["notify"].get_config()}) self._notification = Notify(notify_cfg, local_addrs, debug) self._whitelist = None - if "whitelist" in cfg["args"]: - whitelist_cfg = cfg["args"]["whitelist"] + if "whitelist" in cfg["options"]: + whitelist_cfg = cfg["options"]["whitelist"] whitelist_cfg["name"] = cfg["name"] whitelist_cfg["loglevel"] = cfg["loglevel"] self._whitelist = Conditions( @@ -439,15 +439,15 @@ class Quarantine: debug=debug) self._milter_action = None - if "milter_action" in cfg["args"]: - self._milter_action = cfg["args"]["milter_action"].upper() + if "milter_action" in cfg["options"]: + self._milter_action = cfg["options"]["milter_action"].upper() assert self._milter_action in ["ACCEPT", "REJECT", "DISCARD"], \ f"invalid milter_action '{cfg['args']['milter_action']}'" self._reason = None if self._milter_action == "REJECT": - if "reject_reason" in cfg["args"]: - self._reason = cfg["args"]["reject_reason"] + if "reject_reason" in cfg["options"]: + self._reason = cfg["options"]["reject_reason"] else: self._reason = "Message rejected" @@ -459,9 +459,9 @@ class Quarantine: if self._whitelist is not None: cfg.append(f"whitelist={str(self._whitelist)}") for key in ["milter_action", "reject_reason"]: - if key not in self.cfg["args"]: + if key not in self.cfg["options"]: continue - value = self.cfg["args"][key] + value = self.cfg["options"][key] cfg.append(f"{key}={value}") class_name = type(self).__name__ return f"{class_name}(" + ", ".join(cfg) + ")"