fix actions, storages and mailer shutdown

This commit is contained in:
2021-09-13 14:47:13 +02:00
parent 737a7b555b
commit e1709f763f
4 changed files with 58 additions and 45 deletions

View File

@@ -20,7 +20,7 @@ import os
from pymodmilter import CustomLogger, BaseConfig
from pymodmilter.conditions import ConditionsConfig, Conditions
from pymodmilter import modify, storage
from pymodmilter import modify, notify, storage
class ActionConfig(BaseConfig):
@@ -133,22 +133,26 @@ class ActionConfig(BaseConfig):
elif self["type"] == "notify":
self["headersonly"] = False
self["class"] = notify.EMailNotification
self.add_string_arg(
cfg, ["smtp_host", "envelope_from", "from", "subject",
"template"])
args = ["smtp_host", "envelope_from", "from_header", "subject",
"template"]
if "repl_img" in cfg:
args.append("repl_img")
self.add_string_arg(cfg, args)
self.add_int_arg(cfg, "smtp_port")
if "embedded_imgs" in cfg:
assert isinstance(cfg["embedded_imgs"], list), \
f"{self['name']}: embedded_imgs: invalid value, " \
if "embed_imgs" in cfg:
assert isinstance(cfg["embed_imgs"], list), \
f"{self['name']}: embed_imgs: invalid value, " \
f"should be list"
for img in cfg["embedded_imgs"]:
for img in cfg["embed_imgs"]:
assert isinstance(img, str), \
f"{self['name']}: embedded_imgs: invalid entry, " \
f"{self['name']}: embed_imgs: invalid entry, " \
f"should be string"
self["args"]["embedded_imgs"] = cfg["embedded_imgs"]
self["args"]["embed_imgs"] = cfg["embed_imgs"]
else:
raise RuntimeError(f"{self['name']}: type: invalid action type")

View File

@@ -28,18 +28,14 @@ from urllib.parse import quote
from pymodmilter import mailer
class BaseNotification(object):
class BaseNotification:
"Notification base class"
def __init__(self):
self.logger = logging.getLogger(__name__)
return
def notify(self, msg, qid, mailfrom, recipients,
template_vars=defaultdict(str), synchronous=False):
return
def execute(self, milter, pretend=False,
logger=logging.getLogger(__name__)):
def execute(self, milter, pretend=False, logger=None):
return
@@ -233,9 +229,6 @@ class EMailNotification(BaseNotification):
template_vars=defaultdict(str), synchronous=False,
logger=None):
"Notify recipients via email."
super().notify(
msg, qid, mailfrom, recipients, template_vars, synchronous, logger)
if logger is None:
logger = self.logger
@@ -274,7 +267,7 @@ class EMailNotification(BaseNotification):
"HTML_TEXT": sanitized_text,
"FROM": escape(msg["from"], quote=False),
"ENVELOPE_FROM": escape(mailfrom, quote=False),
"EMAIL_ENVELOPE_FROM_URL": escape(quote(mailfrom),
"ENVELOPE_FROM_URL": escape(quote(mailfrom),
quote=False),
"TO": escape(msg["to"], quote=False),
"ENVELOPE_TO": escape(recipient, quote=False),
@@ -284,40 +277,46 @@ class EMailNotification(BaseNotification):
# parse template
htmltext = self.template.format_map(variables)
msg = MIMEMultipart('related')
msg["From"] = self.from_header.format_map(
defaultdict(str, EMAIL_FROM=msg["from"]))
msg["To"] = msg["to"]
msg["Subject"] = self.subject.format_map(
defaultdict(str, EMAIL_SUBJECT=msg["subject"]))
msg["Date"] = email.utils.formatdate()
msg.attach(MIMEText(htmltext, "html", 'UTF-8'))
newmsg = MIMEMultipart('related')
newmsg["From"] = self.from_header.format_map(
defaultdict(str, FROM=msg["from"]))
newmsg["To"] = msg["to"]
newmsg["Subject"] = self.subject.format_map(
defaultdict(str, SUBJECT=msg["subject"]))
newmsg["Date"] = email.utils.formatdate()
newmsg.attach(MIMEText(htmltext, "html", 'UTF-8'))
if image_replaced:
logger.debug("attaching notification_replacement_img")
msg.attach(self.replacement_img)
newmsg.attach(self.replacement_img)
for img in self.embed_imgs:
logger.debug("attaching imgage")
msg.attach(img)
newmsg.attach(img)
logger.debug(f"sending notification email to: {recipient}")
if synchronous:
try:
mailer.smtp_send(self.smtp_host, self.smtp_port,
self.mailfrom, recipient, msg.as_string())
self.mailfrom, recipient,
newmsg.as_string())
except Exception as e:
raise RuntimeError(
f"error while sending email to '{recipient}': {e}")
else:
mailer.sendmail(self.smtp_host, self.smtp_port, qid,
self.mailfrom, recipient, msg.as_string(),
self.mailfrom, recipient, newmsg.as_string(),
"notification email")
def execute(self, milter, pretend=False,
logger=logging.getLogger(__name__)):
logger=None):
super().execute(milter, pretend, logger)
if logger is None:
logger = self.logger
self.notify(msg=milter.msg, qid=milter.qid,
mailfrom=milter.msginfo["mailfrom"],
recipients=milter.msginfo["rcpts"],
template_vars=milter["msginfo"]["vars"],
template_vars=milter.msginfo["vars"],
logger=logger)

View File

@@ -20,6 +20,7 @@ import logging
import logging.handlers
import sys
from pymodmilter import mailer
from pymodmilter import ModifyMilterConfig, ModifyMilter
from pymodmilter import __version__ as version
@@ -140,11 +141,12 @@ def main():
rc = 0
try:
Milter.runmilter("pymodmilter", socketname=socket, timeout=600)
logger.info("pymodmilter stopped")
except Milter.milter.error as e:
logger.error(e)
rc = 255
mailer.queue.put(None)
logger.info("pymodmilter stopped")
sys.exit(rc)

View File

@@ -22,7 +22,7 @@ from glob import glob
from time import gmtime
class BaseMailStorage(object):
class BaseMailStorage:
"Mail storage base class"
def __init__(self):
return
@@ -69,7 +69,7 @@ class FileMailStorage(BaseMailStorage):
def _get_file_paths(self, storage_id):
datafile = os.path.join(self.directory, storage_id)
metafile = f"{datafile}${self._metadata_suffix}"
metafile = f"{datafile}{self._metadata_suffix}"
return metafile, datafile
def _save_datafile(self, datafile, data):
@@ -90,7 +90,9 @@ class FileMailStorage(BaseMailStorage):
metafile, datafile = self._get_file_paths(storage_id)
try:
if not self.skip_metadata:
os.remove(metafile)
os.remove(datafile)
except IOError as e:
raise RuntimeError(f"unable to remove file: {e}")
@@ -105,7 +107,9 @@ class FileMailStorage(BaseMailStorage):
# save mail
self._save_datafile(datafile, data)
if not self.skip_metadata:
if self.skip_metadata:
metafile = None
else:
# save metadata
metadata = {
"mailfrom": mailfrom,
@@ -146,13 +150,17 @@ class FileMailStorage(BaseMailStorage):
if self.metavar:
milter.msginfo["vars"][f"{self.metavar}_ID"] = storage_id
milter.msginfo["vars"][f"{self.metavar}_METAFILE"] = metafile
milter.msginfo["vars"][f"{self.metavar}_DATAFILE"] = datafile
if not self.skip_metadata:
milter.msginfo["vars"][f"{self.metavar}_METAFILE"] = metafile
def get_metadata(self, storage_id):
"Return metadata of email in storage."
super(FileMailStorage, self).get_metadata(storage_id)
if self.skip_metadata:
return None
metafile, _ = self._get_file_paths(storage_id)
if not os.path.isfile(metafile):
raise RuntimeError(
@@ -211,7 +219,7 @@ class FileMailStorage(BaseMailStorage):
"Delete email from storage."
super(FileMailStorage, self).delete(storage_id, recipients)
if not recipients:
if not recipients or self.skip_metadata:
self._remove(storage_id)
return
@@ -238,9 +246,9 @@ class FileMailStorage(BaseMailStorage):
super(FileMailStorage, self).get_mail(storage_id)
metadata = self.get_metadata(storage_id)
datafile = os.path.join(self.directory, storage_id)
_, datafile = self._get_file_paths(storage_id)
try:
fp = open(datafile, "rb")
data = open(datafile, "rb").read()
except IOError as e:
raise RuntimeError(f"unable to open email data file: {e}")
return (fp, metadata)
return (data, metadata)