[Zope3-checkins] CVS: Zope3/src/zope/app/mail - meta.zcml:1.1.14.2 metaconfigure.py:1.2.2.2 service.py:1.2.2.2

Albertas Agejevas alga@codeworks.lt
Mon, 26 May 2003 14:26:20 -0400


Update of /cvs-repository/Zope3/src/zope/app/mail
In directory cvs.zope.org:/tmp/cvs-serv4417

Modified Files:
      Tag: cw-mail-branch
	meta.zcml metaconfigure.py service.py 
Log Message:
ZCML directives for the mailers.

A thread for queued mail delivery.


=== Zope3/src/zope/app/mail/meta.zcml 1.1.14.1 => 1.1.14.2 ===
--- Zope3/src/zope/app/mail/meta.zcml:1.1.14.1	Thu May 22 04:51:40 2003
+++ Zope3/src/zope/app/mail/meta.zcml	Mon May 26 14:25:49 2003
@@ -28,6 +28,12 @@
         </description>
       </attribute>
 
+      <attribute name="mailer" required="yes">
+        <description>
+	  The name of the mailer used by this service.
+        </description>
+      </attribute>
+
     </directive>
 
     <directive name="directService" handler=".metaconfigure.directService">
@@ -58,51 +64,64 @@
 
     </directive>
 
-    <!-- example of a mailer directive
+    <directive name="sendmailMailer" handler=".metaconfigure.sendmailMailer">
 
-    <directive name="smtp" handler=".metaconfigure.smtp">
+      <description>
+	Registers a new Sendmail mailer.
+      </description>
+
+      <attribute name="name" required="yes">
+	<description>
+	  Name of the mailer.
+        </description>
+      </attribute>
+
+      <attribute name="command" required="no">
+        <description>
+	A template command for sending out mail, containing %(from)s
+        and %(to)s for respective addresses.
+        </description>
+      </attribute>
+
+    </directive>
+
+    <directive name="smtpMailer" handler=".metaconfigure.smtpMailer">
 
       <description>
 	Registers a new SMTP mailer.
       </description>
 
-      <attribute name="id" required="yes">
+      <attribute name="name" required="yes">
 	<description>
-	  ID of the mailer.
+	  Name of the mailer.
         </description>
       </attribute>
 
       <attribute name="hostname" required="no">
         <description>
-	  Name of the server that is used to send the mail. Default is set to
-	  'localhost'.
+	Hostname of the SMTP host.
         </description>
       </attribute>
 
       <attribute name="port" required="no">
         <description>
-	  Port on the server that is used to send the mail. Default is set to
-	  to the standard port '25'.
+	Port of the SMTP server.
         </description>
       </attribute>
 
       <attribute name="username" required="no">
         <description>
-	  Some SMTP servers support authentication. If no username is given,
-          then the Mail Service will not try to use authentication.
+	A username for SMTP AUTH.
         </description>
       </attribute>
 
       <attribute name="password" required="no">
         <description>
-	  Password that is used for authentication. Makes only sense in
-	  combination with username. 
+	A password for SMTP AUTH.
         </description>
       </attribute>
 
     </directive>
-
-    -->
 
   </directives>
 


=== Zope3/src/zope/app/mail/metaconfigure.py 1.2.2.1 => 1.2.2.2 ===
--- Zope3/src/zope/app/mail/metaconfigure.py:1.2.2.1	Thu May 22 04:51:40 2003
+++ Zope3/src/zope/app/mail/metaconfigure.py	Mon May 26 14:25:49 2003
@@ -15,39 +15,71 @@
 
 $Id$
 """
+
 from zope.component import getService
 from zope.configuration.action import Action
 from zope.app.component.metaconfigure import provideService
 from zope.app.mail.service import QueuedMailService, DirectMailService
+from zope.app.mail.service import QueueProcessorThread
+from zope.app.mail.mailer import SendmailMailer, SMTPMailer
 
 
-
-def queuedService(_context, permission, queuePath, name="Mail"):
+def queuedService(_context, permission, queuePath, mailer, name="Mail"):
     # XXX what if queuePath is relative?  I'd like to make it absolute here,
     # but should it be relative to $CWD or $INSTANCE_HOME (if there is one
     # in Zope 3)?
-    component = QueuedMailService(queuePath)
+
+    def createQueuedService():
+        component = QueuedMailService(queuePath)
+        provideService(name, component, permission)
+
+        thread = QueueProcessorThread()
+        thread.setMailer(queryMailer(mailer))
+        thread.setQueuePath(queuePath)
+        thread.setDaemon(True)
+        thread.start()
+
     return [
         Action(
             discriminator = ('service', name),
-            callable = provideService,
-            args = (name, component, permission),
+            callable = createQueuedService,
+            args = (),
             )
         ]
 
 def directService(_context, permission, mailer, name="Mail"):
-    mailer_component = queryMailer(mailer)
-    if mailer_component is None:
-        raise ConfigurationError("Mailer %r is not defined" % mailer)
-    component = DirectMailService(mailer_component)
+
+    def makeService():
+        mailer_component = queryMailer(mailer)
+        if mailer_component is None:
+            raise ConfigurationError("Mailer %r is not defined" % mailer)
+        component = DirectMailService(mailer_component)
+        provideService(name, component, permission)
+
     return [
         Action(
             discriminator = ('service', name),
-            callable = provideService,
-            args = (name, component, permission),
+            callable = makeService,
+            args = (),
             )
         ]
 
+
+def sendmailMailer(_context, name, command):
+    return [Action(discriminator=('mailer', name),
+                   callable=provideMailer,
+                   args=(name, SendmailMailer(command)),)
+        ]
+
+
+def smtpMailer(_context, name, hostname="localhost", port="25",
+               username=None, password=None):
+    return [Action(discriminator=('mailer', name),
+                   callable=provideMailer,
+                   args=(name, SMTPMailer(hostname, port,
+                                          username, password)),)
+        ]
+
 # Example of mailer configuration:
 #
 #   def smtp(_context, id, hostname, port):
@@ -65,6 +97,7 @@
 mailerRegistry = {}
 queryMailer = mailerRegistry.get
 provideMailer = mailerRegistry.__setitem__
+
 
 # Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
 try:


=== Zope3/src/zope/app/mail/service.py 1.2.2.1 => 1.2.2.2 ===
--- Zope3/src/zope/app/mail/service.py:1.2.2.1	Thu May 22 04:51:40 2003
+++ Zope3/src/zope/app/mail/service.py	Mon May 26 14:25:49 2003
@@ -16,11 +16,15 @@
 $Id$
 """
 import rfc822
+import threading
+import os.path
+from os import listdir
 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.interface import implements
 from zope.app.interfaces.mail import IDirectMailService, IQueuedMailService
 from zope.app.mail.maildir import Maildir
@@ -102,5 +106,70 @@
     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.
+    """
+
+    def setMaildir(self, maildir):
+        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 message in self.maildir:
+                    try:
+
+                        fromaddr, toaddrs, message = self._parseMessage(message)
+                        self.mailer.send(fromaddr, toaddrs, message)
+                        # XXX add log reporting
+                    # Blanket except because we don't want this thread to ever die
+                    except:
+                        from traceback import print_exc
+                        print_exc()
+                        # XXX maybe throw away erroring messages here?
+                        # XXX add log reporting
+                        pass
+                else:
+                    sleep(3)
+
+                # A testing plug
+                if not forever:
+                    break