Commit 03524809 authored by Andreas Jung's avatar Andreas Jung

added support for asynchronous mail delivery using zope.sendmail

queue mechanism
parent 0e38c34a
......@@ -66,7 +66,11 @@ Zope Changes
- MailHost: now uses zope.sendmail for delivering the mail providing
integration with the Zope transaction system (avoids sending dupe
emails in case of conflict errors)
emails in case of conflict errors). In addition MailHost now provides
support for asynchronous mail delivery. The 'Use queue' configuration
option will create a mail queue on the filesystem (under
'Queue directory') and start a queue thread that checks the queue every
three seconds. This decouples the sending of mail from its delivery.
- integrated ZODB 3.8
......
......@@ -17,6 +17,7 @@ $Id$
import mimetools
import rfc822
import time
from cStringIO import StringIO
import Acquisition
......@@ -31,10 +32,13 @@ from DateTime import DateTime
from zope.interface import implements
from zope.sendmail.mailer import SMTPMailer
from zope.sendmail.delivery import DirectMailDelivery
from zope.sendmail.delivery import DirectMailDelivery, QueuedMailDelivery, \
QueueProcessorThread
from interfaces import IMailHost
queue_threads = {}
class MailHostError(Exception):
pass
......@@ -64,6 +68,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
security = ClassSecurityInfo()
smtp_uid='' # Class attributes for smooth upgrades
smtp_pwd=''
smtp_queue = False
smtp_queue_directory = '/tmp'
timeout=1.0
......@@ -79,7 +85,7 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
def __init__( self, id='', title='', smtp_host='localhost', smtp_port=25,
smtp_uid='', smtp_pwd=''):
smtp_uid='', smtp_pwd='', smtp_queue=False, smtp_queue_directory='/tmp'):
"""Initialize a new MailHost instance """
self.id = id
self.title = title
......@@ -87,6 +93,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port = int(smtp_port)
self.smtp_uid = smtp_uid
self.smtp_pwd = smtp_pwd
self.smtp_queue = smtp_queue
self.smtp_queue_directory = smtp_queue_directory
# staying for now... (backwards compatibility)
......@@ -95,7 +103,9 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port=smtp_port
security.declareProtected(change_configuration, 'manage_makeChanges')
def manage_makeChanges(self,title,smtp_host,smtp_port,smtp_uid='',smtp_pwd='', REQUEST=None):
def manage_makeChanges(self,title,smtp_host,smtp_port,smtp_uid='',smtp_pwd='',
smtp_queue=False, smtp_queue_directory='/tmp',
REQUEST=None):
'make the changes'
title=str(title)
......@@ -107,6 +117,17 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port=smtp_port
self.smtp_uid = smtp_uid
self.smtp_pwd = smtp_pwd
self.smtp_queue = smtp_queue
self.smtp_queue_directory = smtp_queue_directory
# restart queue processor thread
if self.smtp_queue:
self._stopQueueProcessorThread()
self._startQueueProcessorThread()
else:
self._stopQueueProcessorThread()
if REQUEST is not None:
msg = 'MailHost %s updated' % self.id
return self.manage_main( self
......@@ -153,17 +174,55 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self._send( mfrom, mto, body )
def _makeMailer(self):
""" Create a SMTPMailer """
return SMTPMailer(self.smtp_host,
int(self.smtp_port),
self.smtp_uid or None,
self.smtp_pwd or None
)
def _stopQueueProcessorThread(self):
""" Stop thread for processing the mail queue """
path = self.absolute_url(1)
if queue_threads.has_key(path):
thread = queue_threads[path]
thread.stop()
while thread.isAlive():
# wait until thread is really dead
time.sleep(0.1)
def _startQueueProcessorThread(self):
""" Start thread for processing the mail queue """
path = self.absolute_url(1)
if not queue_threads.has_key(path):
thread = QueueProcessorThread()
thread.setMailer(self._makeMailer())
thread.setQueuePath(self.smtp_queue_directory)
thread.start()
queue_threads[path] = thread
security.declarePrivate('_send')
def _send(self, mfrom, mto, messageText):
""" Send the message """
mailer = SMTPMailer(self.smtp_host,
int(self.smtp_port),
self.smtp_uid or None,
self.smtp_pwd or None
)
delivery = DirectMailDelivery(mailer)
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)
......
......@@ -51,7 +51,7 @@
<tr>
<td align="left" valign="top">
<div class="form-label">
Authentication ID:
Username:
</div>
</td>
<td align="left" valign="top">
......@@ -70,6 +70,29 @@
value="&dtml-smtp_pwd;"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Use queue
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="smtp_queue:boolean" value="1"
<dtml-if "smtp_queue">checked</dtml-if>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Queue directory:
</div>
</td>
<td align="left" valign="top">
<input type="text" name="smtp_queue_directory" size="30"
value="&dtml-smtp_queue_directory;"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment