]> git.ipfire.org Git - ipfire.org.git/commitdiff
messages: Talk to local relay instead of using sendmail
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Jan 2020 09:07:59 +0000 (09:07 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Jan 2020 09:31:57 +0000 (09:31 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/backend/messages.py

index ec7d2d58f874486e5638f6d630b46b6a3b1051b6..1a89d2329e111425d85b3d8629891f38c96e7d65 100644 (file)
@@ -6,6 +6,9 @@ import email.mime.text
 import email.utils
 import logging
 import random
+import smtplib
+import socket
+import ssl
 import subprocess
 import tornado.locale
 import tornado.template
@@ -181,68 +184,80 @@ class Messages(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")
 
+       @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:
-                       self._sendmail(message)
+                       self.send(message)
 
                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")