[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