[Zope3-checkins] CVS: Zope3/src/zope/app/mail - event.py:1.1.14.4 mailer.py:1.3.2.5 service.py:1.2.2.5
Albertas Agejevas
alga@codeworks.lt
Mon, 23 Jun 2003 10:21:13 -0400
Update of /cvs-repository/Zope3/src/zope/app/mail
In directory cvs.zope.org:/tmp/cvs-serv16963/src/zope/app/mail
Modified Files:
Tag: cw-mail-branch
event.py mailer.py service.py
Log Message:
One more sync with HEAD.
=== Zope3/src/zope/app/mail/event.py 1.1.14.3 => 1.1.14.4 ===
--- Zope3/src/zope/app/mail/event.py:1.1.14.3 Sun Jun 22 10:45:17 2003
+++ Zope3/src/zope/app/mail/event.py Mon Jun 23 10:20:12 2003
@@ -15,9 +15,7 @@
$Id$
"""
-from zope.interface import implements
-from zope.app.interfaces.mail import IMailSentEvent, IMailErrorEvent
-__metaclass__ = type
+from zope.app.interfaces.mail import IMailSentEvent
from zope.interface import implements
@@ -26,15 +24,5 @@
implements(IMailSentEvent)
- def __init__(self, messageId):
- self.messageId = messageId
-
-
-class MailErrorEvent:
- __doc__ = IMailErrorEvent.__doc__
-
- implements(IMailErrorEvent)
-
- def __init__(self, messageId, errorMessage):
- self.messageId = messageId
- self.errorMessage = errorMessage
+ def __init__(self, mailer):
+ self.mailer = mailer
=== Zope3/src/zope/app/mail/mailer.py 1.3.2.4 => 1.3.2.5 ===
--- Zope3/src/zope/app/mail/mailer.py:1.3.2.4 Mon Jun 23 07:13:24 2003
+++ Zope3/src/zope/app/mail/mailer.py Mon Jun 23 10:20:12 2003
@@ -11,52 +11,62 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
+"""MailService Implementation
-"""These are classes which abstract different channels an email
-message could be sent out by.
+Simple implementation of the MailService, Mailers and MailEvents.
$Id$
"""
-
-from zope.interface import implements
-from zope.app.interfaces.mail import ISendmailMailer, ISMTPMailer
-from os import popen
from smtplib import SMTP
-__metaclass__ = type
-
-class SendmailMailer:
-
- implements(ISendmailMailer)
-
- # A hook for unit tests
- popen = popen
-
- def __init__(self, command="/usr/lib/sendmail -oem -oi -f %(from)s %(to)s"):
- self.command = command
-
- def send(self, fromaddr, toaddrs, message):
- command = self.command % {'from': fromaddr, 'to': " ".join(toaddrs)}
- f = self.popen(command, "w")
- f.write(message)
- f.close()
-
-class SMTPMailer:
+from zope.app.interfaces.mail import IMailer, IBatchMailer
+from zope.app.event import publish
+from zope.app.mail.event import MailSentEvent
+from zope.interface import implements
- implements(ISMTPMailer)
- smtp = SMTP
+class SimpleMailer:
+ __doc__ = IMailer.__doc__
- def __init__(self, hostname='localhost', port=25,
- username=None, password=None):
- self.hostname = hostname
- self.port = port
- self.username = username
- self.password = password
+ implements(IMailer)
- def send(self, fromaddr, toaddrs, message):
- connection = self.smtp(self.hostname, str(self.port))
- if self.username is not None and self.password is not None:
- connection.login(self.username, self.password)
- connection.sendmail(fromaddr, toaddrs, message)
- connection.quit()
+ def send(self, fromaddr, toaddrs, message,
+ hostname, port, username, password):
+ "See zope.app.interfaces.services.mail.IMailer"
+ server = SMTP(hostname, port)
+ server.set_debuglevel(0)
+ if username is not None and password is not None:
+ server.login(username, password)
+ server.sendmail(fromaddr, toaddrs, message)
+ server.quit()
+ publish(self, MailSentEvent(self))
+
+
+class BatchMailer:
+ __doc__ = IBatchMailer.__doc__
+
+ implements(IBatchMailer)
+
+ # See zope.app.interfaces.mail.IBatchMailer
+ batchDelay = 5000
+
+ # See zope.app.interfaces.mail.IBatchMailer
+ batchSize = 5
+
+ def send(self, fromaddr, toaddrs, message,
+ hostname, port, username, password):
+ "See zope.app.interfaces.mail.IMailer"
+ server = SMTP(hostname, port)
+ server.set_debuglevel(0)
+ if username is not None and password is not None:
+ server.login(username, password)
+ recv = list(toaddrs)
+ batch = []
+ while recv:
+ while len(batch) < self.batchSize and recv:
+ batch.append(recv.pop())
+ server.sendmail(fromaddr, batch, message)
+ batch = []
+ time.sleep(self.batchDelay/1000.0)
+ server.quit()
+ publish(self, MailSentEvent(self))
=== Zope3/src/zope/app/mail/service.py 1.2.2.4 => 1.2.2.5 ===
--- Zope3/src/zope/app/mail/service.py:1.2.2.4 Sun Jun 22 13:47:26 2003
+++ Zope3/src/zope/app/mail/service.py Mon Jun 23 10:20:12 2003
@@ -11,176 +11,59 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Mail service implementation
+"""MailService Implementation
This module contains various implementations of MailServices.
$Id$
"""
-import rfc822
-import threading
-import os.path
-import logging
-from os import listdir, unlink
-from cStringIO import StringIO
-from random import randrange
-from time import strftime
-from socket import gethostname
-from os import getpid
-from time import sleep
+from zope.app.interfaces.mail import IAsyncMailService
from zope.interface import implements
-from zope.app.interfaces.mail import IDirectMailService, IQueuedMailService
-from zope.app.mail.maildir import Maildir
-from transaction.interfaces import IDataManager
-from transaction import get_transaction
-
-__metaclass__ = type
-
-class MailDataManager:
- """XXX I need a docstring"""
-
- implements(IDataManager)
-
- def __init__(self, callable, args=(), onAbort=None):
- self.callable = callable
- self.args = args
- self.onAbort = onAbort
-
- def prepare(self, transaction):
- pass
-
- def abort(self, transaction):
- if self.onAbort:
- self.onAbort()
-
- def commit(self, transaction):
- self.callable(*self.args)
-
- def savepoint(self, transaction):
- pass
-
-
-class AbstractMailService:
-
- def newMessageId(self):
- """Generates a new message ID according to RFC 2822 rules"""
- randmax = 0x7fffffff
- left_part = '%s.%d.%d' % (strftime('%Y%m%d%H%M%S'),
- getpid(),
- randrange(0, randmax))
- return "%s@%s" % (left_part, gethostname())
-
- def send(self, fromaddr, toaddrs, message):
- parser = rfc822.Message(StringIO(message))
- messageid = parser.getheader('Message-Id')
- if messageid:
- if not messageid.startswith('<') or not messageid.endswith('>'):
- raise ValueError('Malformed Message-Id header')
- messageid = messageid[1:-1]
- else:
- messageid = self.newMessageId()
- message = 'Message-Id: <%s>\n%s' % (messageid, message)
- get_transaction().join(self.createDataManager(fromaddr, toaddrs, message))
- return messageid
-
-
-class DirectMailService(AbstractMailService):
- __doc__ = IDirectMailService.__doc__
-
- implements(IDirectMailService)
-
- def __init__(self, mailer):
- self.mailer = mailer
-
- def createDataManager(self, fromaddr, toaddrs, message):
- return MailDataManager(self.mailer.send, args=(fromaddr, toaddrs, message))
-
-
-class QueuedMailService(AbstractMailService):
- __doc__ = IQueuedMailService.__doc__
-
- implements(IQueuedMailService)
-
- def __init__(self, queuePath):
- self._queuePath = queuePath
-
- queuePath = property(lambda self: self._queuePath)
-
- def createDataManager(self, fromaddr, toaddrs, message):
- maildir = Maildir(self.queuePath, True)
- msg = maildir.newMessage()
- msg.write('X-Zope-From: %s\n' % fromaddr)
- msg.write('X-Zope-To: %s\n' % ", ".join(toaddrs))
- msg.write(message)
- return MailDataManager(msg.commit, onAbort=msg.abort)
-
-class QueueProcessorThread(threading.Thread):
- """This thread is started at configuration time from the
- mail:queuedService directive handler.
- """
- log = logging.getLogger("QueueProcessorThread")
-
- def setMaildir(self, maildir):
- """Set the maildir.
-
- This method is used just to provide a maildir stubs ."""
- self.maildir = maildir
-
- def setQueuePath(self, path):
- self.maildir = Maildir(path)
-
- def setMailer(self, mailer):
- self.mailer = mailer
-
- def _parseMessage(self, message):
- """Extract fromaddr and toaddrs from the first two lines of
- the message.
-
- Returns a fromaddr string, a toaddrs tuple and the message
- string.
- """
-
- fromaddr = ""
- toaddrs = ()
- rest = ""
-
- try:
- first, second, rest = message.split('\n', 2)
- except ValueError:
- return fromaddr, toaddrs, message
-
- if first.startswith("X-Zope-From: "):
- i = len("X-Zope-From: ")
- fromaddr = first[i:]
-
- if second.startswith("X-Zope-To: "):
- i = len("X-Zope-To: ")
- toaddrs = tuple(second[i:].split(", "))
-
- return fromaddr, toaddrs, rest
-
- def run(self, forever=True):
- while True:
- for filename in self.maildir:
- try:
- file = open(filename)
- message = file.read()
- file.close()
- fromaddr, toaddrs, message = self._parseMessage(message)
- self.mailer.send(fromaddr, toaddrs, message)
- unlink(filename)
- # XXX maybe log the Message-Id of the message sent
- self.log.info("Mail from %s to %s sent.",
- fromaddr, ", ".join(toaddrs))
- # Blanket except because we don't want this thread to ever die
- except:
- # XXX maybe throw away erroring messages here?
- self.log.error("Error while sending mail from %s to %s.",
- fromaddr, ", ".join(toaddrs), exc_info=1)
- else:
- if forever:
- sleep(3)
-
- # A testing plug
- if not forever:
- break
+
+class AsyncMailService:
+ __doc__ = IAsyncMailService.__doc__
+
+ implements(IAsyncMailService)
+
+ # See zope.app.interfaces.services.mail.IMailService
+ hostname = u''
+
+ # See zope.app.interfaces.services.mail.IMailService
+ port = 25
+
+ # See zope.app.interfaces.services.mail.IMailService
+ username = None
+
+ # See zope.app.interfaces.services.mail.IMailService
+ password = None
+
+ def __init__(self):
+ """Initialize the object."""
+ self.__mailers = {}
+ self.__default_mailer = ''
+
+ def createMailer(self, name):
+ "See zope.app.interfaces.services.mail.IAsyncMailService"
+ return self.__mailers[name]()
+
+ def getMailerNames(self):
+ "See zope.app.interfaces.services.mail.IAsyncMailService"
+ return self.__mailers.keys()
+
+ def getDefaultMailerName(self):
+ "See zope.app.interfaces.services.mail.IAsyncMailService"
+ return self.__default_mailer
+
+ def send(self, fromaddr, toaddrs, message, mailer=None):
+ "See zope.app.interfaces.services.mail.IMailService"
+ if mailer is None:
+ mailer = self.createMailer(self.getDefaultMailerName())
+ # XXX: should be called in new thread:should we use thread or async?
+ mailer.send(fromaddr, toaddrs, message, self.hostname, self.port,
+ self.username, self.password)
+
+ def provideMailer(self, name, klass, default=False):
+ """Add a new mailer to the service."""
+ self.__mailers[name] = klass
+ if default:
+ self.__default_mailer = name