Ability to embed custom images in notifications
This commit is contained in:
10
README.md
10
README.md
@@ -16,7 +16,7 @@ The project is currently in alpha status, but will soon be used in a productive
|
||||
* BeautifulSoup <https://www.crummy.com/software/BeautifulSoup/>
|
||||
|
||||
## Configuration
|
||||
The pyquarantine module uses an INI-style configuration file. The sections are described below.
|
||||
The pyquarantine module uses an INI-style configuration file. The sections are described below. If you have to specify a path in the config, you can always use a relative path to the last loaded config file.
|
||||
|
||||
### Section "global"
|
||||
Any available configuration option can be set in the global section as default instead of in a quarantine section.
|
||||
@@ -85,10 +85,12 @@ The following configuration options are mandatory in each quarantine section:
|
||||
* **notification_email_subject**
|
||||
Notification e-mail subject.
|
||||
* **notification_email_template**
|
||||
Notification e-mail template to use.
|
||||
Path to the notification e-mail template. It is hold in memory during runtime.
|
||||
* **notification_email_replacement_img**
|
||||
An image to replace images in e-mail.
|
||||
|
||||
Path to the image to replace images in e-mails. It is hold in memory during runtime. Leave it empty to disable.
|
||||
* **notification_email_embedded_imgs**
|
||||
Comma-separated list of images to attach to the notification e-mail. The Content-ID of each image will be set to the filename, so you can reference it from the e-mail template. All images are hold in memory during runtime.
|
||||
Leave empty to disable.
|
||||
|
||||
### Actions
|
||||
Every quarantine responds with a milter-action if an e-mail header matches the configured regular expression.
|
||||
|
||||
@@ -94,18 +94,26 @@ notification_email_subject = Spam Quarantine Notification
|
||||
# Notes: Set the template used when sending notification emails.
|
||||
# A relative path to this config file can be used.
|
||||
# This option is needed by notification type 'email'.
|
||||
# Values: [ TEMPLATE_FILE ]
|
||||
# Values: [ TEMPLATE_PATH ]
|
||||
#
|
||||
notification_email_template = templates/notification.template
|
||||
|
||||
# Option: notification_email_replacement_img
|
||||
# Notes: Set the replacement image for images within emails.
|
||||
# Notes: Set the path to the replacement image for img tags within emails.
|
||||
# A relative path to this config file can be used.
|
||||
# This option is needed by notification type 'email'.
|
||||
# Values: [ IMAGE_FILE ]
|
||||
# Values: [ IMAGE_PATH ]
|
||||
#
|
||||
notification_email_replacement_img = templates/removed.png
|
||||
|
||||
# Option: notification_email_embedded_imgs
|
||||
# Notes: Set a list of paths to images to embed in e-mails (comma-separated).
|
||||
# Relative paths to this config file can be used.
|
||||
# This option is needed by notification type 'email'.
|
||||
# Values: [ IMAGE_PATH ]
|
||||
#
|
||||
notification_email_embedded_imgs = templates/logo.png
|
||||
|
||||
# Option: whitelist_type
|
||||
# Notes: Set the whitelist type.
|
||||
# Values: [ db | none ]
|
||||
|
||||
@@ -21,6 +21,7 @@ from cgi import escape
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.image import MIMEImage
|
||||
from os.path import basename
|
||||
|
||||
from pyquarantine import mailer
|
||||
|
||||
@@ -105,7 +106,7 @@ class EMailNotification(BaseNotification):
|
||||
super(EMailNotification, self).__init__(global_config, config, configtest)
|
||||
|
||||
# check if mandatory options are present in config
|
||||
for option in ["smtp_host", "smtp_port", "notification_email_from", "notification_email_subject", "notification_email_template", "notification_email_replacement_img"]:
|
||||
for option in ["smtp_host", "smtp_port", "notification_email_from", "notification_email_subject", "notification_email_template", "notification_email_replacement_img", "notification_email_embedded_imgs"]:
|
||||
if option not in self.config.keys() and option in self.global_config.keys():
|
||||
self.config[option] = self.global_config[option]
|
||||
if option not in self.config.keys():
|
||||
@@ -122,13 +123,31 @@ class EMailNotification(BaseNotification):
|
||||
except IOError as e:
|
||||
raise RuntimeError("error reading template: {}".format(e))
|
||||
|
||||
# read email replacement image
|
||||
try:
|
||||
self.replacement_img = MIMEImage(open(self.config["notification_email_replacement_img"], "rb").read())
|
||||
except IOError as e:
|
||||
raise RuntimeError("error reading replacement image: {}".format(e))
|
||||
# read email replacement image if specified
|
||||
replacement_img_path = self.config["notification_email_replacement_img"].strip()
|
||||
if replacement_img_path:
|
||||
try:
|
||||
self.replacement_img = MIMEImage(open(replacement_img_path, "rb").read())
|
||||
except IOError as e:
|
||||
raise RuntimeError("error reading replacement image: {}".format(e))
|
||||
else:
|
||||
self.replacement_img.add_header("Content-ID", "<removed_for_security_reasons>")
|
||||
else:
|
||||
self.replacement_img.add_header("Content-ID", "<removed_for_security_reasons>")
|
||||
self.replacement_img = None
|
||||
|
||||
# read images to embed if specified
|
||||
embedded_img_paths = [ p.strip() for p in self.config["notification_email_embedded_imgs"].split(",") if p]
|
||||
self.embedded_imgs = []
|
||||
for img_path in embedded_img_paths:
|
||||
# read image
|
||||
try:
|
||||
img = MIMEImage(open(img_path, "rb").read())
|
||||
except IOError as e:
|
||||
raise RuntimeError("error reading image: {}".format(e))
|
||||
else:
|
||||
img.add_header("Content-ID", "<{}>".format(basename(img_path)))
|
||||
self.embedded_imgs.append(img)
|
||||
|
||||
|
||||
def get_text(self, queueid, part):
|
||||
"Get the mail text in html form from email part."
|
||||
@@ -214,12 +233,13 @@ class EMailNotification(BaseNotification):
|
||||
soup = self.get_html_text_part(queueid, email.message_from_binary_file(fp))
|
||||
|
||||
# replace picture sources
|
||||
picture_replaced = False
|
||||
for element in soup("img"):
|
||||
if "src" in element.attrs.keys():
|
||||
self.logger.debug("{}: replacing image: {}".format(queueid, element["src"]))
|
||||
element["src"] = "cid:removed_for_security_reasons"
|
||||
picture_replaced = True
|
||||
image_replaced = False
|
||||
if self.replacement_img:
|
||||
for element in soup("img"):
|
||||
if "src" in element.attrs.keys():
|
||||
self.logger.debug("{}: replacing image: {}".format(queueid, element["src"]))
|
||||
element["src"] = "cid:removed_for_security_reasons"
|
||||
image_replaced = True
|
||||
|
||||
# sanitizing email text of original email
|
||||
sanitized_text = self.sanitize(queueid, soup)
|
||||
@@ -247,10 +267,14 @@ class EMailNotification(BaseNotification):
|
||||
msg["Date"] = email.utils.formatdate()
|
||||
msg.attach(MIMEText(htmltext, "html", 'UTF-8'))
|
||||
|
||||
if picture_replaced:
|
||||
if image_replaced:
|
||||
self.logger.debug("{}: attaching notification_replacement_img".format(queueid))
|
||||
msg.attach(self.replacement_img)
|
||||
|
||||
for img in self.embedded_imgs:
|
||||
self.logger.debug("{}: attaching imgage".format(queueid))
|
||||
msg.attach(img)
|
||||
|
||||
self.logger.debug("{}: sending notification email to: {}".format(queueid, recipient))
|
||||
if synchronous:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user