fix actions, storages and mailer shutdown
This commit is contained in:
@@ -20,7 +20,7 @@ import os
|
|||||||
|
|
||||||
from pymodmilter import CustomLogger, BaseConfig
|
from pymodmilter import CustomLogger, BaseConfig
|
||||||
from pymodmilter.conditions import ConditionsConfig, Conditions
|
from pymodmilter.conditions import ConditionsConfig, Conditions
|
||||||
from pymodmilter import modify, storage
|
from pymodmilter import modify, notify, storage
|
||||||
|
|
||||||
|
|
||||||
class ActionConfig(BaseConfig):
|
class ActionConfig(BaseConfig):
|
||||||
@@ -133,22 +133,26 @@ class ActionConfig(BaseConfig):
|
|||||||
|
|
||||||
elif self["type"] == "notify":
|
elif self["type"] == "notify":
|
||||||
self["headersonly"] = False
|
self["headersonly"] = False
|
||||||
|
self["class"] = notify.EMailNotification
|
||||||
|
|
||||||
self.add_string_arg(
|
args = ["smtp_host", "envelope_from", "from_header", "subject",
|
||||||
cfg, ["smtp_host", "envelope_from", "from", "subject",
|
"template"]
|
||||||
"template"])
|
if "repl_img" in cfg:
|
||||||
|
args.append("repl_img")
|
||||||
|
|
||||||
|
self.add_string_arg(cfg, args)
|
||||||
self.add_int_arg(cfg, "smtp_port")
|
self.add_int_arg(cfg, "smtp_port")
|
||||||
|
|
||||||
if "embedded_imgs" in cfg:
|
if "embed_imgs" in cfg:
|
||||||
assert isinstance(cfg["embedded_imgs"], list), \
|
assert isinstance(cfg["embed_imgs"], list), \
|
||||||
f"{self['name']}: embedded_imgs: invalid value, " \
|
f"{self['name']}: embed_imgs: invalid value, " \
|
||||||
f"should be list"
|
f"should be list"
|
||||||
for img in cfg["embedded_imgs"]:
|
for img in cfg["embed_imgs"]:
|
||||||
assert isinstance(img, str), \
|
assert isinstance(img, str), \
|
||||||
f"{self['name']}: embedded_imgs: invalid entry, " \
|
f"{self['name']}: embed_imgs: invalid entry, " \
|
||||||
f"should be string"
|
f"should be string"
|
||||||
|
|
||||||
self["args"]["embedded_imgs"] = cfg["embedded_imgs"]
|
self["args"]["embed_imgs"] = cfg["embed_imgs"]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f"{self['name']}: type: invalid action type")
|
raise RuntimeError(f"{self['name']}: type: invalid action type")
|
||||||
|
|||||||
@@ -28,18 +28,14 @@ from urllib.parse import quote
|
|||||||
from pymodmilter import mailer
|
from pymodmilter import mailer
|
||||||
|
|
||||||
|
|
||||||
class BaseNotification(object):
|
class BaseNotification:
|
||||||
"Notification base class"
|
"Notification base class"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
return
|
return
|
||||||
|
|
||||||
def notify(self, msg, qid, mailfrom, recipients,
|
def execute(self, milter, pretend=False, logger=None):
|
||||||
template_vars=defaultdict(str), synchronous=False):
|
|
||||||
return
|
|
||||||
|
|
||||||
def execute(self, milter, pretend=False,
|
|
||||||
logger=logging.getLogger(__name__)):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -233,9 +229,6 @@ class EMailNotification(BaseNotification):
|
|||||||
template_vars=defaultdict(str), synchronous=False,
|
template_vars=defaultdict(str), synchronous=False,
|
||||||
logger=None):
|
logger=None):
|
||||||
"Notify recipients via email."
|
"Notify recipients via email."
|
||||||
super().notify(
|
|
||||||
msg, qid, mailfrom, recipients, template_vars, synchronous, logger)
|
|
||||||
|
|
||||||
if logger is None:
|
if logger is None:
|
||||||
logger = self.logger
|
logger = self.logger
|
||||||
|
|
||||||
@@ -274,7 +267,7 @@ class EMailNotification(BaseNotification):
|
|||||||
"HTML_TEXT": sanitized_text,
|
"HTML_TEXT": sanitized_text,
|
||||||
"FROM": escape(msg["from"], quote=False),
|
"FROM": escape(msg["from"], quote=False),
|
||||||
"ENVELOPE_FROM": escape(mailfrom, quote=False),
|
"ENVELOPE_FROM": escape(mailfrom, quote=False),
|
||||||
"EMAIL_ENVELOPE_FROM_URL": escape(quote(mailfrom),
|
"ENVELOPE_FROM_URL": escape(quote(mailfrom),
|
||||||
quote=False),
|
quote=False),
|
||||||
"TO": escape(msg["to"], quote=False),
|
"TO": escape(msg["to"], quote=False),
|
||||||
"ENVELOPE_TO": escape(recipient, quote=False),
|
"ENVELOPE_TO": escape(recipient, quote=False),
|
||||||
@@ -284,40 +277,46 @@ class EMailNotification(BaseNotification):
|
|||||||
# parse template
|
# parse template
|
||||||
htmltext = self.template.format_map(variables)
|
htmltext = self.template.format_map(variables)
|
||||||
|
|
||||||
msg = MIMEMultipart('related')
|
newmsg = MIMEMultipart('related')
|
||||||
msg["From"] = self.from_header.format_map(
|
newmsg["From"] = self.from_header.format_map(
|
||||||
defaultdict(str, EMAIL_FROM=msg["from"]))
|
defaultdict(str, FROM=msg["from"]))
|
||||||
msg["To"] = msg["to"]
|
newmsg["To"] = msg["to"]
|
||||||
msg["Subject"] = self.subject.format_map(
|
newmsg["Subject"] = self.subject.format_map(
|
||||||
defaultdict(str, EMAIL_SUBJECT=msg["subject"]))
|
defaultdict(str, SUBJECT=msg["subject"]))
|
||||||
msg["Date"] = email.utils.formatdate()
|
newmsg["Date"] = email.utils.formatdate()
|
||||||
msg.attach(MIMEText(htmltext, "html", 'UTF-8'))
|
newmsg.attach(MIMEText(htmltext, "html", 'UTF-8'))
|
||||||
|
|
||||||
if image_replaced:
|
if image_replaced:
|
||||||
logger.debug("attaching notification_replacement_img")
|
logger.debug("attaching notification_replacement_img")
|
||||||
msg.attach(self.replacement_img)
|
newmsg.attach(self.replacement_img)
|
||||||
|
|
||||||
for img in self.embed_imgs:
|
for img in self.embed_imgs:
|
||||||
logger.debug("attaching imgage")
|
logger.debug("attaching imgage")
|
||||||
msg.attach(img)
|
newmsg.attach(img)
|
||||||
|
|
||||||
logger.debug(f"sending notification email to: {recipient}")
|
logger.debug(f"sending notification email to: {recipient}")
|
||||||
if synchronous:
|
if synchronous:
|
||||||
try:
|
try:
|
||||||
mailer.smtp_send(self.smtp_host, self.smtp_port,
|
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:
|
except Exception as e:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"error while sending email to '{recipient}': {e}")
|
f"error while sending email to '{recipient}': {e}")
|
||||||
else:
|
else:
|
||||||
mailer.sendmail(self.smtp_host, self.smtp_port, qid,
|
mailer.sendmail(self.smtp_host, self.smtp_port, qid,
|
||||||
self.mailfrom, recipient, msg.as_string(),
|
self.mailfrom, recipient, newmsg.as_string(),
|
||||||
"notification email")
|
"notification email")
|
||||||
|
|
||||||
def execute(self, milter, pretend=False,
|
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,
|
self.notify(msg=milter.msg, qid=milter.qid,
|
||||||
mailfrom=milter.msginfo["mailfrom"],
|
mailfrom=milter.msginfo["mailfrom"],
|
||||||
recipients=milter.msginfo["rcpts"],
|
recipients=milter.msginfo["rcpts"],
|
||||||
template_vars=milter["msginfo"]["vars"],
|
template_vars=milter.msginfo["vars"],
|
||||||
logger=logger)
|
logger=logger)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import logging
|
|||||||
import logging.handlers
|
import logging.handlers
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from pymodmilter import mailer
|
||||||
from pymodmilter import ModifyMilterConfig, ModifyMilter
|
from pymodmilter import ModifyMilterConfig, ModifyMilter
|
||||||
from pymodmilter import __version__ as version
|
from pymodmilter import __version__ as version
|
||||||
|
|
||||||
@@ -140,11 +141,12 @@ def main():
|
|||||||
rc = 0
|
rc = 0
|
||||||
try:
|
try:
|
||||||
Milter.runmilter("pymodmilter", socketname=socket, timeout=600)
|
Milter.runmilter("pymodmilter", socketname=socket, timeout=600)
|
||||||
logger.info("pymodmilter stopped")
|
|
||||||
except Milter.milter.error as e:
|
except Milter.milter.error as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
rc = 255
|
rc = 255
|
||||||
|
|
||||||
|
mailer.queue.put(None)
|
||||||
|
logger.info("pymodmilter stopped")
|
||||||
sys.exit(rc)
|
sys.exit(rc)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from glob import glob
|
|||||||
from time import gmtime
|
from time import gmtime
|
||||||
|
|
||||||
|
|
||||||
class BaseMailStorage(object):
|
class BaseMailStorage:
|
||||||
"Mail storage base class"
|
"Mail storage base class"
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
return
|
||||||
@@ -69,7 +69,7 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
|
|
||||||
def _get_file_paths(self, storage_id):
|
def _get_file_paths(self, storage_id):
|
||||||
datafile = os.path.join(self.directory, 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
|
return metafile, datafile
|
||||||
|
|
||||||
def _save_datafile(self, datafile, data):
|
def _save_datafile(self, datafile, data):
|
||||||
@@ -90,7 +90,9 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
metafile, datafile = self._get_file_paths(storage_id)
|
metafile, datafile = self._get_file_paths(storage_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(metafile)
|
if not self.skip_metadata:
|
||||||
|
os.remove(metafile)
|
||||||
|
|
||||||
os.remove(datafile)
|
os.remove(datafile)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise RuntimeError(f"unable to remove file: {e}")
|
raise RuntimeError(f"unable to remove file: {e}")
|
||||||
@@ -105,7 +107,9 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
# save mail
|
# save mail
|
||||||
self._save_datafile(datafile, data)
|
self._save_datafile(datafile, data)
|
||||||
|
|
||||||
if not self.skip_metadata:
|
if self.skip_metadata:
|
||||||
|
metafile = None
|
||||||
|
else:
|
||||||
# save metadata
|
# save metadata
|
||||||
metadata = {
|
metadata = {
|
||||||
"mailfrom": mailfrom,
|
"mailfrom": mailfrom,
|
||||||
@@ -146,13 +150,17 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
|
|
||||||
if self.metavar:
|
if self.metavar:
|
||||||
milter.msginfo["vars"][f"{self.metavar}_ID"] = storage_id
|
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
|
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):
|
def get_metadata(self, storage_id):
|
||||||
"Return metadata of email in storage."
|
"Return metadata of email in storage."
|
||||||
super(FileMailStorage, self).get_metadata(storage_id)
|
super(FileMailStorage, self).get_metadata(storage_id)
|
||||||
|
|
||||||
|
if self.skip_metadata:
|
||||||
|
return None
|
||||||
|
|
||||||
metafile, _ = self._get_file_paths(storage_id)
|
metafile, _ = self._get_file_paths(storage_id)
|
||||||
if not os.path.isfile(metafile):
|
if not os.path.isfile(metafile):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
@@ -211,7 +219,7 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
"Delete email from storage."
|
"Delete email from storage."
|
||||||
super(FileMailStorage, self).delete(storage_id, recipients)
|
super(FileMailStorage, self).delete(storage_id, recipients)
|
||||||
|
|
||||||
if not recipients:
|
if not recipients or self.skip_metadata:
|
||||||
self._remove(storage_id)
|
self._remove(storage_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -238,9 +246,9 @@ class FileMailStorage(BaseMailStorage):
|
|||||||
super(FileMailStorage, self).get_mail(storage_id)
|
super(FileMailStorage, self).get_mail(storage_id)
|
||||||
|
|
||||||
metadata = self.get_metadata(storage_id)
|
metadata = self.get_metadata(storage_id)
|
||||||
datafile = os.path.join(self.directory, storage_id)
|
_, datafile = self._get_file_paths(storage_id)
|
||||||
try:
|
try:
|
||||||
fp = open(datafile, "rb")
|
data = open(datafile, "rb").read()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise RuntimeError(f"unable to open email data file: {e}")
|
raise RuntimeError(f"unable to open email data file: {e}")
|
||||||
return (fp, metadata)
|
return (data, metadata)
|
||||||
|
|||||||
Reference in New Issue
Block a user