fix actions, storages and mailer shutdown
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
os.remove(metafile)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user