Package turbomail :: Package transports :: Module smtp
[hide private]
[frames] | no frames]

Source Code for Module turbomail.transports.smtp

  1  # encoding: utf-8 
  2   
  3  """Deliver messages using (E)SMTP.""" 
  4   
  5   
  6  import logging 
  7  import socket 
  8   
  9  from smtplib import SMTP, SMTPException, SMTPRecipientsRefused, SMTPSenderRefused, SMTPServerDisconnected 
 10   
 11  from turbomail.api import Transport, TransportFactory 
 12  from turbomail.compat import get_message 
 13  from turbomail.control import interface 
 14  from turbomail.exceptions import MailConfigurationException, TransportExhaustedException 
 15   
 16   
 17  __all__ = ['load'] 
 18   
 19  log = logging.getLogger("turbomail.transport") 
 20  deliverylog = logging.getLogger("turbomail.delivery") 
 21   
 22   
 23   
24 -def load():
25 return SMTPTransportFactory()
26 27
28 -class SMTPTransport(Transport):
29 - def __init__(self):
30 super(SMTPTransport, self).__init__() 31 log.debug("SMTPTransport created.") 32 33 self.server = self.config_get('mail.smtp.server', None) 34 if self.server == None: 35 raise MailConfigurationException('no server configured for smtp ("mail.smtp.server")') 36 self.username = self.config_get("mail.smtp.username") 37 self.password = self.config_get("mail.smtp.password") 38 self.use_tls = self.config_get("mail.smtp.tls", None) 39 self.debug = self.config_get("mail.smtp.debug", False) 40 41 self.max_number_of_messages_per_connection = \ 42 self.config_get('mail.smtp.max_messages_per_connection', default=1, tm2_key='jobs') 43 44 self.connection = None 45 self.nr_messages_sent_with_this_connection = None
46
47 - def close_connection(self):
48 if self.is_connected(): 49 log.debug("Closing SMTP connection.") 50 try: 51 try: 52 self.connection.quit() 53 except SMTPServerDisconnected: 54 pass 55 except (SMTPException, socket.error), e: 56 msg = 'Exception occured when stopping connection' + unicode(e) 57 log.exception(msg) 58 finally: 59 self.connection = None
60
61 - def stop(self):
62 super(SMTPTransport, self).stop() 63 self.close_connection()
64
65 - def is_connected(self):
66 return getattr(self.connection, 'sock', None) is not None
67
68 - def _encrypt_connection_with_tls_if_configured(self, connection):
69 # TODO: Testcase: Always use ehlo for possibly encrypted connections. 70 connection.ehlo() 71 72 server_provides_tls = connection.has_extn('STARTTLS') 73 # TODO: Testcase self.tls = False (-> no TLS) 74 if (self.use_tls is None) and (not server_provides_tls): 75 log.info("TLS unavailable. Messages will be delivered insecurely.") 76 return 77 elif self.use_tls == False: 78 return 79 connection.starttls() 80 connection.ehlo() 81 log.info("TLS enabled on SMTP server.")
82
83 - def connect_to_server(self):
84 connection = SMTP() 85 connection.set_debuglevel(self.debug) 86 log.info("Connecting to SMTP server %s." % self.server) 87 connection.connect(self.server) 88 89 self._encrypt_connection_with_tls_if_configured(connection) 90 if self.username and self.password: 91 log.info("Authenticating as %s." % self.username) 92 connection.login(self.username, self.password) 93 return connection
94
96 if not self.is_connected(): 97 self.connection = self.connect_to_server() 98 self.nr_messages_sent_with_this_connection = 0
99
101 return self.nr_messages_sent_with_this_connection < self.max_number_of_messages_per_connection
102
103 - def send_with_smtp(self, message):
104 try: 105 self.nr_messages_sent_with_this_connection += 1 106 sender = str(message.envelope_sender) 107 recipients = message.recipients.string_addresses 108 self.connection.sendmail(sender, recipients, str(message)) 109 except SMTPSenderRefused, e: 110 # The envelope sender was refused. This is bad. 111 deliverylog.error("%s REFUSED %s %s" % (message.id, e.__class__.__name__, get_message(e))) 112 raise 113 except SMTPRecipientsRefused, e: 114 # All recipients were refused. Log which recipients. 115 # This allows you to automatically parse your logs for bad e-mail addresses. 116 deliverylog.warning("%s REFUSED %s %s" % (message.id, e.__class__.__name__, get_message(e))) 117 raise 118 except SMTPServerDisconnected, e: 119 raise TransportExhaustedException 120 except Exception, e: 121 cls_name = e.__class__.__name__ 122 deliverylog.debug("%s EXCEPTION %s" % (message.id, cls_name), exc_info=True) 123 124 if message.nr_retries >= 0: 125 deliverylog.warning("%s DEFERRED %s" % (message.id, cls_name)) 126 message.nr_retries -= 1 127 interface.manager.deliver(message) 128 return 129 else: 130 deliverylog.error("%s REFUSED %s" % (message.id, cls_name), exc_info=True) 131 raise
132
133 - def deliver(self, message):
134 log.info("Attempting delivery of message %s." % message.id) 135 deliverylog.info("%s DELIVER" % message.id) 136 137 self.connect_to_server_if_nessary() 138 try: 139 self.send_with_smtp(message) 140 finally: 141 if not self.can_send_more_messages_on_this_connection(): 142 self.close_connection() 143 deliverylog.info("%s SENT" % message.id)
144 145
146 -class SMTPTransportFactory(TransportFactory):
147 name = "smtp" 148 transport = SMTPTransport
149