Add dynamic template variables according to regex
This commit is contained in:
@@ -79,6 +79,13 @@ The following configuration options are mandatory in each quarantine section:
|
||||
* **{EMAIL_HTML_TEXT}**
|
||||
Sanitized version of the e-mail text part of the original e-mail. Only harmless HTML tags and attributes are included. Images are replaced with the image set by notification_email_replacement_img option.
|
||||
|
||||
Some template variables are only available if the regex of the matching quarantine contains subgroups or named subgroups (python syntax). This is useful to include information (e.g. virus names, spam points, ...) of the matching header within the notification.
|
||||
The following dynamic tempalte variables are available:
|
||||
* **{SUBGROUP_n}**
|
||||
Content of a subgroup, 'n' will be replaced by the index number of each subgroup, starting with 0.
|
||||
* **{subgroup_name}**
|
||||
Content of a named subgroup, 'subgroup_name' will be replaced by its name.
|
||||
|
||||
The following configuration options are mandatory for this notification type:
|
||||
* **notification_email_from**
|
||||
Notification e-mail from-address.
|
||||
|
||||
@@ -119,9 +119,13 @@ class QuarantineMilter(Milter.Base):
|
||||
|
||||
# check email header against quarantine regex
|
||||
self.logger.debug("{}: {}: checking header against regex '{}'".format(self.queueid, quarantine["name"], quarantine["regex"]))
|
||||
if quarantine["regex_compiled"].match(header):
|
||||
match = quarantine["regex_compiled"].search(header)
|
||||
if match:
|
||||
self.logger.debug("{}: {}: header matched regex".format(self.queueid, quarantine["name"]))
|
||||
|
||||
if "subgroups" not in quarantine.keys():
|
||||
# save subgroups of match into the quarantine object for later use as template variables
|
||||
quarantine["subgroups"] = match.groups(default="")
|
||||
quarantine["named_subgroups"] = match.groupdict(default="")
|
||||
# check for whitelisted recipients
|
||||
whitelist = quarantine["whitelist_obj"]
|
||||
if whitelist != None:
|
||||
@@ -212,7 +216,8 @@ class QuarantineMilter(Milter.Base):
|
||||
# add email to quarantine
|
||||
self.logger.info("{}: adding to quarantine '{}' for: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
||||
try:
|
||||
quarantine_id = quarantine["quarantine_obj"].add(self.queueid, self.mailfrom, recipients, self.subject, fp=self.fp)
|
||||
quarantine_id = quarantine["quarantine_obj"].add(self.queueid, self.mailfrom, recipients, self.subject, self.fp,
|
||||
quarantine["subgroups"], quarantine["named_subgroups"])
|
||||
except RuntimeError as e:
|
||||
self.logger.error("{}: unable to add to quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
||||
return Milter.TEMPFAIL
|
||||
@@ -222,7 +227,8 @@ class QuarantineMilter(Milter.Base):
|
||||
# notify
|
||||
self.logger.info("{}: sending notification for quarantine '{}' to: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
||||
try:
|
||||
quarantine["notification_obj"].notify(self.queueid, quarantine_id, self.subject, self.mailfrom, recipients, fp=self.fp)
|
||||
quarantine["notification_obj"].notify(self.queueid, quarantine_id, self.subject, self.mailfrom, recipients, self.fp,
|
||||
quarantine["subgroups"], quarantine["named_subgroups"])
|
||||
except RuntimeError as e:
|
||||
self.logger.error("{}: unable to send notification for quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
||||
return Milter.TEMPFAIL
|
||||
@@ -316,7 +322,7 @@ def generate_milter_config(configtest=False, config_files=[]):
|
||||
|
||||
# pre-compile regex
|
||||
logger.debug("{}: compiling regex '{}'".format(quarantine_name, config["regex"]))
|
||||
config["regex_compiled"] = re.compile(config["regex"])
|
||||
config["regex_compiled"] = re.compile(config["regex"], re.MULTILINE + re.DOTALL)
|
||||
|
||||
# create quarantine instance
|
||||
quarantine_type = config["quarantine_type"].lower()
|
||||
|
||||
@@ -33,7 +33,7 @@ class BaseNotification(object):
|
||||
self.config = config
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def notify(self, queueid, quarantine_id, subject, mailfrom, recipients, fp, synchronous=False):
|
||||
def notify(self, queueid, quarantine_id, subject, mailfrom, recipients, fp, subgroups=None, named_subgroups=None, synchronous=False):
|
||||
fp.seek(0)
|
||||
pass
|
||||
|
||||
@@ -224,9 +224,9 @@ class EMailNotification(BaseNotification):
|
||||
|
||||
return soup
|
||||
|
||||
def notify(self, queueid, quarantine_id, subject, mailfrom, recipients, fp, synchronous=False):
|
||||
def notify(self, queueid, quarantine_id, subject, mailfrom, recipients, fp, subgroups=None, named_subgroups=None, synchronous=False):
|
||||
"Notify recipients via email."
|
||||
super(EMailNotification, self).notify(queueid, quarantine_id, subject, mailfrom, recipients, fp, synchronous)
|
||||
super(EMailNotification, self).notify(queueid, quarantine_id, subject, mailfrom, recipients, fp, subgroups, named_subgroups, synchronous)
|
||||
|
||||
# extract html text from email
|
||||
self.logger.debug("{}: extraction email text from original email".format(queueid))
|
||||
@@ -244,21 +244,29 @@ class EMailNotification(BaseNotification):
|
||||
# sanitizing email text of original email
|
||||
sanitized_text = self.sanitize(queueid, soup)
|
||||
|
||||
# escape possible html entities in subject
|
||||
subject = escape(subject)
|
||||
|
||||
# sending email notifications
|
||||
for recipient in recipients:
|
||||
self.logger.debug("{}: generating notification email for '{}'".format(queueid, recipient))
|
||||
self.logger.debug("{}: parsing email template".format(queueid))
|
||||
|
||||
htmltext = self.template.format( \
|
||||
EMAIL_HTML_TEXT=sanitized_text, \
|
||||
EMAIL_FROM=escape(mailfrom), \
|
||||
EMAIL_TO=escape(recipient), \
|
||||
EMAIL_SUBJECT=subject, \
|
||||
EMAIL_QUARANTINE_ID=quarantine_id
|
||||
)
|
||||
# generate dict containing all template variables
|
||||
variables = {
|
||||
"EMAIL_HTML_TEXT": sanitized_text,
|
||||
"EMAIL_FROM": escape(mailfrom),
|
||||
"EMAIL_TO": escape(recipient),
|
||||
"EMAIL_SUBJECT": escape(subject),
|
||||
"EMAIL_QUARANTINE_ID": quarantine_id
|
||||
}
|
||||
if subgroups:
|
||||
number = 0
|
||||
for subgroup in subgroups:
|
||||
variables["SUBGROUP_{}".format(number)] = escape(subgroup)
|
||||
if named_subgroups:
|
||||
for key, value in named_subgroups.items(): named_subgroups[key] = escape(value)
|
||||
variables.update(named_subgroups)
|
||||
|
||||
# parse template
|
||||
htmltext = self.template.format(**variables)
|
||||
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg["Subject"] = self.subject
|
||||
|
||||
@@ -33,7 +33,7 @@ class BaseQuarantine(object):
|
||||
self.config = config
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def add(self, queueid, mailfrom, recipients, subject, fp):
|
||||
def add(self, queueid, mailfrom, recipients, subject, fp, subgroups=None, named_subgroups=None):
|
||||
"Add email to quarantine."
|
||||
fp.seek(0)
|
||||
return ""
|
||||
@@ -109,9 +109,9 @@ class FileQuarantine(BaseQuarantine):
|
||||
except IOError as e:
|
||||
raise RuntimeError("unable to remove data file: {}".format(e))
|
||||
|
||||
def add(self, queueid, mailfrom, recipients, subject, fp):
|
||||
def add(self, queueid, mailfrom, recipients, subject, fp, subgroups=None, named_subgroups=None):
|
||||
"Add email to file quarantine and return quarantine-id."
|
||||
super(FileQuarantine, self).add(queueid, mailfrom, recipients, subject, fp)
|
||||
super(FileQuarantine, self).add(queueid, mailfrom, recipients, subject, fp, subgroups, named_subgroups)
|
||||
quarantine_id = "{}_{}".format(datetime.now().strftime("%Y%m%d%H%M%S"), queueid)
|
||||
|
||||
# save mail
|
||||
@@ -123,7 +123,9 @@ class FileQuarantine(BaseQuarantine):
|
||||
"recipients": recipients,
|
||||
"subject": subject,
|
||||
"date": timegm(gmtime()),
|
||||
"queue_id": queueid
|
||||
"queue_id": queueid,
|
||||
"subgroups": subgroups,
|
||||
"named_subgroups": named_subgroups
|
||||
}
|
||||
try:
|
||||
self._save_metafile(quarantine_id, metadata)
|
||||
@@ -223,7 +225,8 @@ class FileQuarantine(BaseQuarantine):
|
||||
datafile = os.path.join(self.directory, quarantine_id)
|
||||
try:
|
||||
with open(datafile, "rb") as fp:
|
||||
self.config["notification_obj"].notify(metadata["queue_id"], quarantine_id, metadata["subject"], metadata["mailfrom"], recipients, fp, synchronous=True)
|
||||
self.config["notification_obj"].notify(metadata["queue_id"], quarantine_id, metadata["subject"], metadata["mailfrom"], recipients, fp,
|
||||
metadata["subgroups"], metadata["named_subgroups"], synchronous=True)
|
||||
except IOError as e:
|
||||
raise(RuntimeError("unable to read data file: {}".format(e)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user