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}**
|
* **{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.
|
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:
|
The following configuration options are mandatory for this notification type:
|
||||||
* **notification_email_from**
|
* **notification_email_from**
|
||||||
Notification e-mail from-address.
|
Notification e-mail from-address.
|
||||||
|
|||||||
@@ -119,9 +119,13 @@ class QuarantineMilter(Milter.Base):
|
|||||||
|
|
||||||
# check email header against quarantine regex
|
# check email header against quarantine regex
|
||||||
self.logger.debug("{}: {}: checking header against regex '{}'".format(self.queueid, quarantine["name"], 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"]))
|
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
|
# check for whitelisted recipients
|
||||||
whitelist = quarantine["whitelist_obj"]
|
whitelist = quarantine["whitelist_obj"]
|
||||||
if whitelist != None:
|
if whitelist != None:
|
||||||
@@ -212,7 +216,8 @@ class QuarantineMilter(Milter.Base):
|
|||||||
# add email to quarantine
|
# add email to quarantine
|
||||||
self.logger.info("{}: adding to quarantine '{}' for: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
self.logger.info("{}: adding to quarantine '{}' for: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
||||||
try:
|
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:
|
except RuntimeError as e:
|
||||||
self.logger.error("{}: unable to add to quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
self.logger.error("{}: unable to add to quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
||||||
return Milter.TEMPFAIL
|
return Milter.TEMPFAIL
|
||||||
@@ -222,7 +227,8 @@ class QuarantineMilter(Milter.Base):
|
|||||||
# notify
|
# notify
|
||||||
self.logger.info("{}: sending notification for quarantine '{}' to: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
self.logger.info("{}: sending notification for quarantine '{}' to: {}".format(self.queueid, quarantine["name"], ", ".join(recipients)))
|
||||||
try:
|
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:
|
except RuntimeError as e:
|
||||||
self.logger.error("{}: unable to send notification for quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
self.logger.error("{}: unable to send notification for quarantine '{}': {}".format(self.queueid, quarantine["name"], e))
|
||||||
return Milter.TEMPFAIL
|
return Milter.TEMPFAIL
|
||||||
@@ -316,7 +322,7 @@ def generate_milter_config(configtest=False, config_files=[]):
|
|||||||
|
|
||||||
# pre-compile regex
|
# pre-compile regex
|
||||||
logger.debug("{}: compiling regex '{}'".format(quarantine_name, config["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
|
# create quarantine instance
|
||||||
quarantine_type = config["quarantine_type"].lower()
|
quarantine_type = config["quarantine_type"].lower()
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class BaseNotification(object):
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.logger = logging.getLogger(__name__)
|
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)
|
fp.seek(0)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -224,9 +224,9 @@ class EMailNotification(BaseNotification):
|
|||||||
|
|
||||||
return soup
|
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."
|
"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
|
# extract html text from email
|
||||||
self.logger.debug("{}: extraction email text from original email".format(queueid))
|
self.logger.debug("{}: extraction email text from original email".format(queueid))
|
||||||
@@ -244,21 +244,29 @@ class EMailNotification(BaseNotification):
|
|||||||
# sanitizing email text of original email
|
# sanitizing email text of original email
|
||||||
sanitized_text = self.sanitize(queueid, soup)
|
sanitized_text = self.sanitize(queueid, soup)
|
||||||
|
|
||||||
# escape possible html entities in subject
|
|
||||||
subject = escape(subject)
|
|
||||||
|
|
||||||
# sending email notifications
|
# sending email notifications
|
||||||
for recipient in recipients:
|
for recipient in recipients:
|
||||||
self.logger.debug("{}: generating notification email for '{}'".format(queueid, recipient))
|
self.logger.debug("{}: generating notification email for '{}'".format(queueid, recipient))
|
||||||
self.logger.debug("{}: parsing email template".format(queueid))
|
self.logger.debug("{}: parsing email template".format(queueid))
|
||||||
|
|
||||||
htmltext = self.template.format( \
|
# generate dict containing all template variables
|
||||||
EMAIL_HTML_TEXT=sanitized_text, \
|
variables = {
|
||||||
EMAIL_FROM=escape(mailfrom), \
|
"EMAIL_HTML_TEXT": sanitized_text,
|
||||||
EMAIL_TO=escape(recipient), \
|
"EMAIL_FROM": escape(mailfrom),
|
||||||
EMAIL_SUBJECT=subject, \
|
"EMAIL_TO": escape(recipient),
|
||||||
EMAIL_QUARANTINE_ID=quarantine_id
|
"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 = MIMEMultipart('alternative')
|
||||||
msg["Subject"] = self.subject
|
msg["Subject"] = self.subject
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class BaseQuarantine(object):
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.logger = logging.getLogger(__name__)
|
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."
|
"Add email to quarantine."
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
return ""
|
return ""
|
||||||
@@ -109,9 +109,9 @@ class FileQuarantine(BaseQuarantine):
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise RuntimeError("unable to remove data file: {}".format(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."
|
"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)
|
quarantine_id = "{}_{}".format(datetime.now().strftime("%Y%m%d%H%M%S"), queueid)
|
||||||
|
|
||||||
# save mail
|
# save mail
|
||||||
@@ -123,7 +123,9 @@ class FileQuarantine(BaseQuarantine):
|
|||||||
"recipients": recipients,
|
"recipients": recipients,
|
||||||
"subject": subject,
|
"subject": subject,
|
||||||
"date": timegm(gmtime()),
|
"date": timegm(gmtime()),
|
||||||
"queue_id": queueid
|
"queue_id": queueid,
|
||||||
|
"subgroups": subgroups,
|
||||||
|
"named_subgroups": named_subgroups
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
self._save_metafile(quarantine_id, metadata)
|
self._save_metafile(quarantine_id, metadata)
|
||||||
@@ -223,7 +225,8 @@ class FileQuarantine(BaseQuarantine):
|
|||||||
datafile = os.path.join(self.directory, quarantine_id)
|
datafile = os.path.join(self.directory, quarantine_id)
|
||||||
try:
|
try:
|
||||||
with open(datafile, "rb") as fp:
|
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:
|
except IOError as e:
|
||||||
raise(RuntimeError("unable to read data file: {}".format(e)))
|
raise(RuntimeError("unable to read data file: {}".format(e)))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user