class Mailer(object): """ Manages sending of email messages. :param host: SMTP hostname :param port: SMTP port :param username: SMTP username :param password: SMPT password :param tls: use TLS :param ssl: use SSL :param keyfile: SSL key file :param certfile: SSL certificate file :param queue_path: path to maildir for queued messages :param default_sender: default "from" address :param sendmail_app: path to "sendmail" binary. repoze defaults to "/usr/sbin/sendmail" :param sendmail_template: custom commandline template passed to sendmail binary, defaults to'["{sendmail_app}", "-t", "-i", "-f", "{sender}"]' :param debug: SMTP debug level """ def __init__(self, host='localhost', port=25, username=None, password=None, tls=False, ssl=False, keyfile=None, certfile=None, queue_path=None, default_sender=None, sendmail_app=None, sendmail_template=None, debug=0): if ssl: self.smtp_mailer = SMTP_SSLMailer( hostname=host, port=port, username=username, password=password, no_tls=not(tls), force_tls=tls, debug_smtp=debug, keyfile=keyfile, certfile=certfile) else: self.smtp_mailer = SMTPMailer( hostname=host, port=port, username=username, password=password, no_tls=not(tls), force_tls=tls, debug_smtp=debug) self.direct_delivery = DirectMailDelivery(self.smtp_mailer) if queue_path: self.queue_delivery = QueuedMailDelivery(queue_path) else: self.queue_delivery = None self.sendmail_mailer = SendmailMailer(sendmail_app, sendmail_template) self.sendmail_delivery = DirectMailDelivery(self.sendmail_mailer) self.default_sender = default_sender @classmethod def from_settings(cls, settings, prefix='mail.'): """ Creates a new instance of **Mailer** from settings dict. :param settings: a settings dict-like :param prefix: prefix separating **pyramid_mailer** settings """ settings = settings or {} kwarg_names = [prefix + k for k in ( 'host', 'port', 'username', 'password', 'tls', 'ssl', 'keyfile', 'certfile', 'queue_path', 'debug', 'default_sender')] size = len(prefix) kwargs = dict(((k[size:], settings[k]) for k in settings.keys() if k in kwarg_names)) for key in ('tls', 'ssl'): val = kwargs.get(key) if val: kwargs[key] = asbool(val) return cls(**kwargs) def send(self, message): """ Sends a message. The message is handled inside a transaction, so in case of failure (or the message fails) the message will not be sent. :param message: a **Message** instance. """ return self.direct_delivery.send(*self._message_args(message)) def send_immediately(self, message, fail_silently=False): """ Sends a message immediately, outside the transaction manager. If there is a connection error to the mail server this will have to be handled manually. However if you pass ``fail_silently`` the error will be swallowed. :versionadded: 0.3 :param message: a **Message** instance. :param fail_silently: silently handle connection errors. """ try: return self.smtp_mailer.send(*self._message_args(message)) except smtplib.socket.error: if not fail_silently: raise def send_to_queue(self, message): """ Adds a message to a maildir queue. In order to handle this, the setting **mail.queue_path** must be provided and must point to a valid maildir. :param message: a **Message** instance. """ if not self.queue_delivery: raise RuntimeError("No queue_path provided") return self.queue_delivery.send(*self._message_args(message)) def _message_args(self, message): message.sender = message.sender or self.default_sender # convert Lamson message to Python email package msessage msg = message.to_message() return (message.sender, message.send_to, msg) def send_sendmail(self, message ): """ Sends a message within the transaction manager. Uses the local sendmail option :param message: a **Message** instance. """ return self.sendmail_delivery.send(*self._message_args(message)) def send_immediately_sendmail(self, message, fail_silently=False): """ Sends a message immediately, outside the transaction manager. Uses the local sendmail option If there is a connection error to the mail server this will have to be handled manually. However if you pass ``fail_silently`` the error will be swallowed. :param message: a **Message** instance. :param fail_silently: silently handle connection errors. """ try: return self.sendmail_mailer.send(*self._message_args(message)) except: if not fail_silently: raise
class Mailer(object): """Manages sending of email messages. :param host: SMTP hostname :param port: SMTP port :param username: SMTP username :param password: SMPT password :param tls: use TLS :param ssl: use SSL :param keyfile: SSL key file :param certfile: SSL certificate file :param queue_path: path to maildir for queued messages :param default_sender: default "from" address :param sendmail_app: path to "sendmail" binary. repoze defaults to "/usr/sbin/sendmail" :param sendmail_template: custom commandline template passed to sendmail binary, defaults to'["{sendmail_app}", "-t", "-i", "-f", "{sender}"]' :param debug: SMTP debug level """ def __init__(self, host='localhost', port=25, username=None, password=None, tls=False, ssl=False, keyfile=None, certfile=None, queue_path=None, default_sender=None, sendmail_app=None, sendmail_template=None, debug=0): if ssl: self.smtp_mailer = SMTP_SSLMailer( hostname=host, port=port, username=username, password=password, no_tls=not(tls), force_tls=tls, debug_smtp=debug, keyfile=keyfile, certfile=certfile) else: self.smtp_mailer = SMTPMailer( hostname=host, port=port, username=username, password=password, no_tls=not(tls), force_tls=tls, debug_smtp=debug) self.direct_delivery = DirectMailDelivery(self.smtp_mailer) if queue_path: self.queue_delivery = QueuedMailDelivery(queue_path) else: self.queue_delivery = None self.sendmail_mailer = SendmailMailer(sendmail_app, sendmail_template) self.sendmail_delivery = DirectMailDelivery(self.sendmail_mailer) self.default_sender = default_sender @classmethod def from_settings(cls, settings, prefix='mail.'): """Create a new instance of 'Mailer' from settings dict. :param settings: a settings dict-like :param prefix: prefix separating 'pyramid_mailer' settings """ settings = settings or {} kwarg_names = [prefix + k for k in ( 'host', 'port', 'username', 'password', 'tls', 'ssl', 'keyfile', 'certfile', 'queue_path', 'debug', 'default_sender')] size = len(prefix) kwargs = dict(((k[size:], settings[k]) for k in settings.keys() if k in kwarg_names)) for key in ('tls', 'ssl'): val = kwargs.get(key) if val: kwargs[key] = asbool(val) return cls(**kwargs) def send(self, message): """Send a message. The message is handled inside a transaction, so in case of failure (or the message fails) the message will not be sent. :param message: a 'Message' instance. """ return self.direct_delivery.send(*self._message_args(message)) def send_immediately(self, message, fail_silently=False): """Send a message immediately, outside the transaction manager. If there is a connection error to the mail server this will have to be handled manually. However if you pass ``fail_silently`` the error will be swallowed. :versionadded: 0.3 :param message: a 'Message' instance. :param fail_silently: silently handle connection errors. """ try: return self.smtp_mailer.send(*self._message_args(message)) except smtplib.socket.error: if not fail_silently: raise def send_to_queue(self, message): """Add a message to a maildir queue. In order to handle this, the setting 'mail.queue_path' must be provided and must point to a valid maildir. :param message: a 'Message' instance. """ if not self.queue_delivery: raise RuntimeError("No queue_path provided") return self.queue_delivery.send(*self._message_args(message)) def _message_args(self, message): message.sender = message.sender or self.default_sender # convert Lamson message to Python email package msessage msg = message.to_message() return (message.sender, message.send_to, msg) def send_sendmail(self, message ): """Send a message within the transaction manager. Uses the local sendmail option :param message: a 'Message' instance. """ return self.sendmail_delivery.send(*self._message_args(message)) def send_immediately_sendmail(self, message, fail_silently=False): """Send a message immediately, outside the transaction manager. Uses the local sendmail option If there is a connection error to the mail server this will have to be handled manually. However if you pass ``fail_silently`` the error will be swallowed. :param message: a 'Message' instance. :param fail_silently: silently handle connection errors. """ try: return self.sendmail_mailer.send(*self._message_args(message)) except: if not fail_silently: raise
class TestSMTPMailer(unittest.TestCase): def setUp(self, port=None): global SMTP class SMTP(object): fail_on_quit = False def __init__(myself, h, p): myself.hostname = h myself.port = p myself.quitted = False myself.closed = False if type(p) == type(u""): raise socket.error("Int or String expected") self.smtp = myself def sendmail(self, f, t, m): self.fromaddr = f self.toaddrs = t self.msgtext = m def login(self, username, password): self.username = username self.password = password def quit(self): if self.fail_on_quit: raise socket.sslerror("dang") self.quitted = True self.close() def close(self): self.closed = True def has_extn(self, ext): return True def ehlo(self): self.does_esmtp = True return (200, 'Hello, I am your stupid MTA mock') def starttls(self): pass if port is None: self.mailer = SMTPMailer() else: self.mailer = SMTPMailer(u'localhost', port) self.mailer.smtp = SMTP def test_interface(self): verifyObject(ISMTPMailer, self.mailer) def test_send(self): for run in (1, 2): if run == 2: self.setUp(u'25') fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(self.smtp.quitted) self.assert_(self.smtp.closed) def test_send_auth(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.username = '******' self.mailer.password = '******' self.mailer.hostname = 'spamrelay' self.mailer.port = 31337 self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.username, 'foo') self.assertEquals(self.smtp.password, 'evil') self.assertEquals(self.smtp.hostname, 'spamrelay') self.assertEquals(self.smtp.port, '31337') self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(self.smtp.quitted) self.assert_(self.smtp.closed) def test_send_failQuit(self): self.mailer.smtp.fail_on_quit = True try: fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(not self.smtp.quitted) self.assert_(self.smtp.closed) finally: self.mailer.smtp.fail_on_quit = False def test_destroy_SMTP_connection_on_abort(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.vote(fromaddr, toaddrs, msgtext) self.assertFalse(self.mailer.connection is None) self.mailer.abort() self.assertTrue(self.mailer.connection is None) def test_destroy_SMTP_connection_on_commit(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.vote(fromaddr, toaddrs, msgtext) self.assertFalse(self.mailer.connection is None) self.mailer.send(fromaddr, toaddrs, msgtext) self.assertTrue(self.mailer.connection is None)
class TestSMTPMailer(unittest.TestCase): def setUp(self, port=None): global SMTP class SMTP(object): fail_on_quit = False def __init__(myself, h, p): myself.hostname = h myself.port = p myself.quitted = False myself.closed = False if type(p) == type(u""): raise socket.error("Int or String expected") self.smtp = myself def sendmail(self, f, t, m): self.fromaddr = f self.toaddrs = t self.msgtext = m def login(self, username, password): self.username = username self.password = password def quit(self): if self.fail_on_quit: raise socket.sslerror("dang") self.quitted = True self.close() def close(self): self.closed = True def has_extn(self, ext): return True def ehlo(self): self.does_esmtp = True return (200, 'Hello, I am your stupid MTA mock') def starttls(self): pass if port is None: self.mailer = SMTPMailer() else: self.mailer = SMTPMailer(u'localhost', port) self.mailer.smtp = SMTP def test_interface(self): verifyObject(ISMTPMailer, self.mailer) def test_send(self): for run in (1,2): if run == 2: self.setUp(u'25') fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(self.smtp.quitted) self.assert_(self.smtp.closed) def test_send_auth(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.username = '******' self.mailer.password = '******' self.mailer.hostname = 'spamrelay' self.mailer.port = 31337 self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.username, 'foo') self.assertEquals(self.smtp.password, 'evil') self.assertEquals(self.smtp.hostname, 'spamrelay') self.assertEquals(self.smtp.port, '31337') self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(self.smtp.quitted) self.assert_(self.smtp.closed) def test_send_failQuit(self): self.mailer.smtp.fail_on_quit = True try: fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.send(fromaddr, toaddrs, msgtext) self.assertEquals(self.smtp.fromaddr, fromaddr) self.assertEquals(self.smtp.toaddrs, toaddrs) self.assertEquals(self.smtp.msgtext, msgtext) self.assert_(not self.smtp.quitted) self.assert_(self.smtp.closed) finally: self.mailer.smtp.fail_on_quit = False def test_destroy_SMTP_connection_on_abort(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.vote(fromaddr, toaddrs, msgtext) self.assertFalse(self.mailer.connection is None) self.mailer.abort() self.assertTrue(self.mailer.connection is None) def test_destroy_SMTP_connection_on_commit(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' self.mailer.vote(fromaddr, toaddrs, msgtext) self.assertFalse(self.mailer.connection is None) self.mailer.send(fromaddr, toaddrs, msgtext) self.assertTrue(self.mailer.connection is None)
class Mailer(object): """ Manages sending of email messages. :param host: SMTP hostname :param port: SMTP port :param username: SMTP username :param password: SMPT password :param tls: use TLS :param ssl: use SSL :param keyfile: SSL key file :param certfile: SSL certificate file :param queue_path: path to maildir for queued messages :param default_sender: default "from" address :param debug: SMTP debug level """ def __init__(self, host='localhost', port=25, username=None, password=None, tls=False, ssl=False, keyfile=None, certfile=None, queue_path=None, default_sender=None, debug=0): if ssl: self.smtp_mailer = SMTP_SSLMailer(hostname=host, port=port, username=username, password=password, no_tls=not (tls), force_tls=tls, debug_smtp=debug, keyfile=keyfile, certfile=certfile) else: self.smtp_mailer = SMTPMailer(hostname=host, port=port, username=username, password=password, no_tls=not (tls), force_tls=tls, debug_smtp=debug) self.direct_delivery = DirectMailDelivery(self.smtp_mailer) if queue_path: self.queue_delivery = QueuedMailDelivery(queue_path) else: self.queue_delivery = None self.default_sender = default_sender @classmethod def from_settings(cls, settings, prefix='mail.'): """ Creates a new instance of **Message** from settings dict. :param settings: a settings dict-like :param prefix: prefix separating **pyramid_mailer** settings """ settings = settings or {} kwarg_names = [ prefix + k for k in ('host', 'port', 'username', 'password', 'tls', 'ssl', 'keyfile', 'certfile', 'queue_path', 'debug', 'default_sender') ] size = len(prefix) kwargs = dict(((k[size:], settings[k]) for k in settings.keys() if k in kwarg_names)) return cls(**kwargs) def send(self, message): """ Sends a message. The message is handled inside a transaction, so in case of failure (or the message fails) the message will not be sent. :param message: a **Message** instance. """ return self.direct_delivery.send(*self._message_args(message)) def send_immediately(self, message, fail_silently=False): """ Sends a message immediately, outside the transaction manager. If there is a connection error to the mail server this will have to be handled manually. However if you pass ``fail_silently`` the error will be swallowed. :versionadded: 0.3 :param message: a **Message** instance. :param fail_silently: silently handle connection errors. """ try: return self.smtp_mailer.send(*self._message_args(message)) except smtplib.socket.error: if not fail_silently: raise def send_to_queue(self, message): """ Adds a message to a maildir queue. In order to handle this, the setting **mail.queue_path** must be provided and must point to a valid maildir. :param message: a **Message** instance. """ if not self.queue_delivery: raise RuntimeError, "No queue_path provided" return self.queue_delivery.send(*self._message_args(message)) def _message_args(self, message): message.sender = message.sender or self.default_sender return (message.sender, message.send_to, message.to_message())
class TestSMTPMailer(unittest.TestCase): def setUp(self, port=None): self.ehlo_status = 200 self.extns = set(['starttls',]) global SMTP class SMTP(object): fail_on_quit = False def __init__(myself, h, p): myself.hostname = h myself.port = p myself.quitted = False myself.closed = False myself.debuglevel = 0 self.smtp = myself def set_debuglevel(self, lvl): self.debuglevel = bool(lvl) def sendmail(self, f, t, m): self.fromaddr = f self.toaddrs = t self.msgtext = m def login(self, username, password): self.username = username self.password = password def quit(self): if self.fail_on_quit: raise SSLError("dang") self.quitted = True self.close() def close(self): self.closed = True def has_extn(myself, ext): return ext in self.extns def ehlo(myself): myself.does_esmtp = True return (self.ehlo_status, 'Hello, I am your stupid MTA mock') helo = ehlo def starttls(self): pass if port is None: self.mailer = SMTPMailer() else: self.mailer = SMTPMailer('localhost', port) self.mailer.smtp = SMTP def test_send(self): from email.message import Message for run in (1,2): if run == 2: self.setUp('25') fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msg = Message() msg['Headers'] = 'headers' msg.set_payload('bodybodybody\n-- \nsig\n') self.mailer.send(fromaddr, toaddrs, msg) self.assertEqual(self.smtp.fromaddr, fromaddr) self.assertEqual(self.smtp.toaddrs, toaddrs) self.assertEqual( self.smtp.msgtext, msg.as_string().encode('ascii')) self.assertTrue(self.smtp.quitted) self.assertTrue(self.smtp.closed) def test_fail_ehlo(self): from email.message import Message fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msg = Message() self.ehlo_status = 100 self.assertRaises(RuntimeError, self.mailer.send, fromaddr, toaddrs, msg) def test_tls_required_not_available(self): from email.message import Message fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') msg = Message() self.extns.remove('starttls') self.mailer.force_tls = True self.assertRaises(RuntimeError, self.mailer.send, fromaddr, toaddrs, msg) def test_send_auth(self): fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') headers = 'Headers: headers' body='bodybodybody\n-- \nsig\n' msgtext = headers+'\n\n'+body msg = email.message_from_string(msgtext) self.mailer.username = '******' self.mailer.password = '******' self.mailer.hostname = 'spamrelay' self.mailer.port = 31337 self.mailer.send(fromaddr, toaddrs, msg) self.assertEqual(self.smtp.username, 'foo') self.assertEqual(self.smtp.password, 'evil') self.assertEqual(self.smtp.hostname, 'spamrelay') self.assertEqual(self.smtp.port, '31337') self.assertEqual(self.smtp.fromaddr, fromaddr) self.assertEqual(self.smtp.toaddrs, toaddrs) self.assertTrue(body.encode('ascii') in self.smtp.msgtext) self.assertTrue(headers.encode('ascii') in self.smtp.msgtext) self.assertTrue(self.smtp.quitted) self.assertTrue(self.smtp.closed) def test_send_failQuit(self): self.mailer.smtp.fail_on_quit = True try: fromaddr = '*****@*****.**' toaddrs = ('*****@*****.**', '*****@*****.**') headers = 'Headers: headers' body='bodybodybody\n-- \nsig\n' msgtext = headers+'\n\n'+body msg = email.message_from_string(msgtext) self.mailer.send(fromaddr, toaddrs, msg) self.assertEqual(self.smtp.fromaddr, fromaddr) self.assertEqual(self.smtp.toaddrs, toaddrs) self.assertTrue(body.encode('ascii') in self.smtp.msgtext) self.assertTrue(headers.encode('ascii') in self.smtp.msgtext) self.assertTrue(not self.smtp.quitted) self.assertTrue(self.smtp.closed) finally: self.mailer.smtp.fail_on_quit = False