Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
import email.utils
import logging
import random
import email.utils
import logging
import random
+import smtplib
+import socket
+import ssl
import subprocess
import tornado.locale
import tornado.template
import subprocess
import tornado.locale
import tornado.template
class Queue(misc.Object):
class Queue(misc.Object):
+ context = ssl.create_default_context()
+
@property
def messages(self):
return self.db.query("SELECT * FROM messages \
WHERE time_sent IS NULL \
ORDER BY priority DESC, time_created ASC")
@property
def messages(self):
return self.db.query("SELECT * FROM messages \
WHERE time_sent IS NULL \
ORDER BY priority DESC, time_created ASC")
+ @lazy_property
+ def relay(self):
+ """
+ Connection to the local mail relay
+ """
+ hostname = socket.getfqdn()
+
+ # Open SMTP connection
+ conn = smtplib.SMTP(hostname)
+
+ # Start TLS connection
+ conn.starttls(context=self.context)
+
+ return conn
+
async def send_all(self):
# Sends all messages
for message in self.messages:
async def send_all(self):
# Sends all messages
for message in self.messages:
- self._sendmail(message)
logging.debug("All messages sent")
logging.debug("All messages sent")
- def _sendmail(self, message):
+ def send(self, message):
- Delivers the given message to sendmail.
+ Delivers the given message the local mail relay
- try:
- # Parse the message from what is in the database
- msg = email.message_from_string(message.message)
+ # Parse the message from what is in the database
+ msg = email.message_from_string(message.message)
- logging.debug("Sending a message %s to: %s" % (
- msg.get("Subject"), ", ".join(message.envelope_recipients)
- ))
+ logging.debug("Sending a message %s to: %s" % (
+ msg.get("Subject"), ", ".join(message.envelope_recipients)
+ ))
- # Make sendmail command line
- cmd = [
- "/usr/sbin/sendmail",
+ error_messages = []
+ rejected_recipients = {}
- # Don't treat a single line with . as end of input
- "-oi",
-
- # Envelope Sender
- "-f", msg.get("From") or "no-reply@ipfire.org",
- ]
-
- # Envelope Recipients
- cmd += message.envelope_recipients
+ # Try delivering the email
+ try:
+ rejected_recipients = self.relay.send_message(msg,
+ to_addrs=message.envelope_recipients)
- # Run sendmail and pipe the email in
- p = subprocess.Popen(cmd, bufsize=0, close_fds=True,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ except smtplib.SMTPRecipientsRefused as e:
+ rejected_recipients = e.recipients
- stdout, stderr = p.communicate(message.message.encode("utf-8"))
+ except smtplib.SMTPException as e:
+ logging.error("SMTP Exception: %s" % e)
+ error_messages.append("%s" % e)
- # Wait until sendmail has finished
- p.wait()
+ # Log all emails that could not be delivered
+ for recipient in rejected_recipients:
+ code, reason = rejected_recipients[recipient]
- if p.returncode:
- self.db.execute("UPDATE messages SET error_message = %s \
- WHERE id = %s", stdout, message.id)
+ error_messages.append("Recipient refused: %s - %s (%s)" % \
+ (recipient, code, reason.decode()))
- logging.error("Could not send mail: %s" % stdout)
+ if error_messages:
+ self.db.execute("UPDATE messages SET error_message = %s \
+ WHERE id = %s", "; ".join(error_messages), message.id)
- # Raise all exceptions
- except:
- raise
+ logging.error("Could not send email: %s" % message.id)
+ for line in error_messages:
+ logging.error(line)
- else:
- # After the email has been successfully sent, we mark it as such
- self.db.execute("UPDATE messages SET time_sent = NOW() \
- WHERE id = %s", message.id)
+ # After the email has been successfully sent, we mark it as such
+ self.db.execute("UPDATE messages SET time_sent = NOW() \
+ WHERE id = %s", message.id)
def cleanup(self):
logging.debug("Cleaning up message queue")
def cleanup(self):
logging.debug("Cleaning up message queue")