Skip to content

Commit

Permalink
- Launchpad #246748: added 'immediate' option to sendXXX() methods
Browse files Browse the repository at this point in the history
        for sending a mail immediately by-passing the zope.sendmail delivery
        mechanism
  • Loading branch information
zopyx committed Jul 13, 2008
1 parent fb2610e commit b687cd0
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 17 deletions.
37 changes: 20 additions & 17 deletions MailHost.py
Expand Up @@ -155,15 +155,16 @@ def manage_makeChanges(self,title,smtp_host,smtp_port,smtp_uid='',smtp_pwd='',
security.declareProtected(use_mailhost_services, 'sendTemplate')
def sendTemplate(trueself, self, messageTemplate,
statusTemplate=None, mto=None, mfrom=None,
encode=None, REQUEST=None):
encode=None, REQUEST=None, immediate=False):
'render a mail template, then send it...'
mtemplate = getattr(self, messageTemplate)
messageText = mtemplate(self, trueself.REQUEST)
messageText, mto, mfrom = _mungeHeaders( messageText, mto, mfrom)
messageText=_encode(messageText, encode)
trueself._send(mfrom, mto, messageText)
trueself._send(mfrom, mto, messageText, immediate)

if not statusTemplate: return "SEND OK"
if not statusTemplate:
return "SEND OK"

try:
stemplate=getattr(self, statusTemplate)
Expand All @@ -173,23 +174,23 @@ def sendTemplate(trueself, self, messageTemplate,

security.declareProtected(use_mailhost_services, 'send')
def send(self, messageText, mto=None, mfrom=None, subject=None,
encode=None):
encode=None, immediate=False):

messageText, mto, mfrom = _mungeHeaders( messageText, mto, mfrom, subject)
messageText = _encode(messageText, encode)
self._send(mfrom, mto, messageText)
self._send(mfrom, mto, messageText, immediate)

# This is here for backwards compatibility only. Possibly it could
# be used to send messages at a scheduled future time, or via a mail queue?
security.declareProtected(use_mailhost_services, 'scheduledSend')
scheduledSend = send

security.declareProtected(use_mailhost_services, 'simple_send')
def simple_send(self, mto, mfrom, subject, body):
def simple_send(self, mto, mfrom, subject, body, immediate=False):
body="From: %s\nTo: %s\nSubject: %s\n\n%s" % (
mfrom, mto, subject, body)

self._send( mfrom, mto, body )
self._send(mfrom, mto, body, immediate)


def _makeMailer(self):
Expand Down Expand Up @@ -218,7 +219,7 @@ def _stopQueueProcessorThread(self):
@synchronized(lock)
def _startQueueProcessorThread(self):
""" Start thread for processing the mail queue """

path = self.absolute_url(1)
if not queue_threads.has_key(path):
thread = QueueProcessorThread()
Expand All @@ -228,7 +229,6 @@ def _startQueueProcessorThread(self):
queue_threads[path] = thread
LOG.info('Thread for %s started' % path)


security.declareProtected(view, 'queueLength')
def queueLength(self):
""" return length of mail queue """
Expand Down Expand Up @@ -268,17 +268,20 @@ def manage_restartQueueThread(self, action='start', REQUEST=None):


security.declarePrivate('_send')
def _send(self, mfrom, mto, messageText):
def _send(self, mfrom, mto, messageText, immediate=False):
""" Send the message """

if self.smtp_queue:
# Start queue processor thread, if necessary
self._startQueueProcessorThread()
delivery = QueuedMailDelivery(self.smtp_queue_directory)
if immediate:
self._makeMailer().send(mfrom, mto, messageText)
else:
delivery = DirectMailDelivery(self._makeMailer())

delivery.send(mfrom, mto, messageText)
if self.smtp_queue:
# Start queue processor thread, if necessary
self._startQueueProcessorThread()
delivery = QueuedMailDelivery(self.smtp_queue_directory)
else:
delivery = DirectMailDelivery(self._makeMailer())

delivery.send(mfrom, mto, messageText)

InitializeClass(MailBase)

Expand Down
218 changes: 218 additions & 0 deletions tests/testMailHost.py
@@ -0,0 +1,218 @@
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""MailHost unit tests.
$Id$
"""

import unittest

from Products.MailHost.MailHost import MailHost
from Products.MailHost.MailHost import MailHostError, _mungeHeaders


class DummyMailHost(MailHost):
meta_type = 'Dummy Mail Host'
def __init__(self, id):
self.id = id
self.sent = ''
def _send(self, mfrom, mto, messageText, immediate=False):
self.sent = messageText
self.immediate = immediate


class TestMailHost(unittest.TestCase):

def _getTargetClass(self):
return DummyMailHost

def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def test_z3interfaces(self):
from Products.MailHost.interfaces import IMailHost
from zope.interface.verify import verifyClass

verifyClass(IMailHost, self._getTargetClass())

def testAllHeaders( self ):
msg = """To: recipient@domain.com
From: sender@domain.com
Subject: This is the subject
This is the message body."""
# No additional info
resmsg, resto, resfrom = _mungeHeaders( msg )
self.failUnless(resto == ['recipient@domain.com'])
self.failUnless(resfrom == 'sender@domain.com' )

# Add duplicated info
resmsg, resto, resfrom = _mungeHeaders(msg, 'recipient@domain.com',
'sender@domain.com', 'This is the subject' )
self.failUnless(resto == ['recipient@domain.com'])
self.failUnless(resfrom == 'sender@domain.com' )

# Add extra info
resmsg, resto, resfrom = _mungeHeaders(msg, 'recipient2@domain.com',
'sender2@domain.com', 'This is the real subject' )
self.failUnless(resto == ['recipient2@domain.com'])
self.failUnless(resfrom == 'sender2@domain.com' )

def testMissingHeaders( self ):
msg = """X-Header: Dummy header
This is the message body."""
# Doesn't specify to
self.failUnlessRaises(MailHostError, _mungeHeaders, msg,
mfrom='sender@domain.com')
# Doesn't specify from
self.failUnlessRaises(MailHostError, _mungeHeaders, msg,
mto='recipient@domain.com')

def testNoHeaders( self ):
msg = """This is the message body."""
# Doesn't specify to
self.failUnlessRaises(MailHostError, _mungeHeaders, msg,
mfrom='sender@domain.com')
# Doesn't specify from
self.failUnlessRaises(MailHostError, _mungeHeaders, msg,
mto='recipient@domain.com')
# Specify all
resmsg, resto, resfrom = _mungeHeaders(msg, 'recipient2@domain.com',
'sender2@domain.com', 'This is the real subject')
self.failUnless(resto == ['recipient2@domain.com'])
self.failUnless(resfrom == 'sender2@domain.com' )

def testBCCHeader( self ):
msg = "From: me@example.com\nBcc: many@example.com\n\nMessage text"
# Specify only the "Bcc" header. Useful for bulk emails.
resmsg, resto, resfrom = _mungeHeaders(msg)
self.failUnless(resto == ['many@example.com'])
self.failUnless(resfrom == 'me@example.com' )


def testAddressParser( self ):
msg = """To: "Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>
CC: "Web, Jack" <jack@web.com>
From: sender@domain.com
Subject: This is the subject
This is the message body."""

# Test Address-Parser for To & CC given in messageText

resmsg, resto, resfrom = _mungeHeaders( msg )
self.failUnless(resto == ['"Name, Nick" <recipient@domain.com>',
'"Foo Bar" <foo@domain.com>',
'"Web, Jack" <jack@web.com>'])
self.failUnless(resfrom == 'sender@domain.com' )

# Test Address-Parser for a given mto-string

resmsg, resto, resfrom = _mungeHeaders(msg, mto= '"Public, Joe" <pjoe@domain.com>, "Foo Bar" <foo@domain.com>')

self.failUnless(resto == ['"Public, Joe" <pjoe@domain.com>',
'"Foo Bar" <foo@domain.com>'])
self.failUnless(resfrom == 'sender@domain.com' )

def testSendMessageOnly(self):
msg = """\
To: "Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>
From: sender@domain.com
Subject: This is the subject
Date: Sun, 27 Aug 2006 17:00:00 +0200
This is the message body."""

mailhost = self._makeOne('MailHost')
mailhost.send(msg)
self.assertEqual(mailhost.sent, msg)

def testSendWithArguments(self):
inmsg = """\
Date: Sun, 27 Aug 2006 17:00:00 +0200
This is the message body."""

outmsg = """\
Date: Sun, 27 Aug 2006 17:00:00 +0200
Subject: This is the subject
To: "Name, Nick" <recipient@domain.com>,"Foo Bar" <foo@domain.com>
From: sender@domain.com
This is the message body."""

mailhost = self._makeOne('MailHost')
mailhost.send(messageText=inmsg,
mto='"Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>',
mfrom='sender@domain.com', subject='This is the subject')
self.assertEqual(mailhost.sent, outmsg)

def testSendWithMtoList(self):
inmsg = """\
Date: Sun, 27 Aug 2006 17:00:00 +0200
This is the message body."""

outmsg = """\
Date: Sun, 27 Aug 2006 17:00:00 +0200
Subject: This is the subject
To: "Name, Nick" <recipient@domain.com>,"Foo Bar" <foo@domain.com>
From: sender@domain.com
This is the message body."""

mailhost = self._makeOne('MailHost')
mailhost.send(messageText=inmsg,
mto=['"Name, Nick" <recipient@domain.com>', '"Foo Bar" <foo@domain.com>'],
mfrom='sender@domain.com', subject='This is the subject')
self.assertEqual(mailhost.sent, outmsg)

def testSimpleSend(self):
outmsg = """\
From: sender@domain.com
To: "Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>
Subject: This is the subject
This is the message body."""

mailhost = self._makeOne('MailHost')
mailhost.simple_send(mto='"Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>',
mfrom='sender@domain.com', subject='This is the subject',
body='This is the message body.')
self.assertEqual(mailhost.sent, outmsg)
self.assertEqual(mailhost.immediate, False)

def testSendImmediate(self):
outmsg = """\
From: sender@domain.com
To: "Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>
Subject: This is the subject
This is the message body."""

mailhost = self._makeOne('MailHost')
mailhost.simple_send(mto='"Name, Nick" <recipient@domain.com>, "Foo Bar" <foo@domain.com>',
mfrom='sender@domain.com', subject='This is the subject',
body='This is the message body.', immediate=True)
self.assertEqual(mailhost.sent, outmsg)
self.assertEqual(mailhost.immediate, True)


def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( TestMailHost ) )
return suite

if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

0 comments on commit b687cd0

Please sign in to comment.