[Zope3-checkins] CVS: Zope3/src/zope/app/mail - configure.zcml:1.1.14.1 event.py:1.1.14.1 meta.zcml:1.1.14.1 metaconfigure.py:1.2.2.1 service.py:1.2.2.1 mail.py:NONE mailer.py:NONE
Viktorija Zaksiene
ryzaja@codeworks.lt
Thu, 22 May 2003 04:52:11 -0400
Update of /cvs-repository/Zope3/src/zope/app/mail
In directory cvs.zope.org:/tmp/cvs-serv25415/src/zope/app/mail
Modified Files:
Tag: cw-mail-branch
configure.zcml event.py meta.zcml metaconfigure.py service.py
Removed Files:
Tag: cw-mail-branch
mail.py mailer.py
Log Message:
Marius and Viktorija.
Refactored Mail delivery service. The work is still not finished, so we
commit it to the branch.
=== Zope3/src/zope/app/mail/configure.zcml 1.1 => 1.1.14.1 ===
--- Zope3/src/zope/app/mail/configure.zcml:1.1 Wed Apr 16 09:45:43 2003
+++ Zope3/src/zope/app/mail/configure.zcml Thu May 22 04:51:40 2003
@@ -1,21 +1,8 @@
<zopeConfigure
xmlns="http://namespaces.zope.org/zope"
- xmlns:service="http://namespaces.zope.org/service"
- xmlns:mail="http://namespaces.zope.org/mail"
>
-<serviceType id="Mail"
+<serviceType id="Mail"
interface="zope.app.interfaces.mail.IMailService" />
-
-<mail:mailservice name="Mail"
- hostname="localhost" port="25"
- class=".service.AsyncMailService"
- permission="zope.Public"/>
-
-<mail:mailer name="SimpleMailer" class=".mailer.SimpleMailer"
- serviceType="Mail" default="True" />
-
-<mail:mailer name="BatchMailer" class=".mailer.BatchMailer"
- serviceType="Mail" />
</zopeConfigure>
=== Zope3/src/zope/app/mail/event.py 1.1 => 1.1.14.1 ===
--- Zope3/src/zope/app/mail/event.py:1.1 Wed Apr 16 09:45:43 2003
+++ Zope3/src/zope/app/mail/event.py Thu May 22 04:51:40 2003
@@ -15,13 +15,28 @@
$Id$
"""
+from zope.interface import implements
from zope.app.interfaces.mail import IMailSentEvent
+from zope.app.interfaces.mail import IMailErrorEvent
+
+__metaclass__ = type
class MailSentEvent:
__doc__ = IMailSentEvent.__doc__
- __implements__ = IMailSentEvent
+ 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/meta.zcml 1.1 => 1.1.14.1 ===
--- Zope3/src/zope/app/mail/meta.zcml:1.1 Wed Apr 16 09:45:43 2003
+++ Zope3/src/zope/app/mail/meta.zcml Thu May 22 04:51:40 2003
@@ -1,12 +1,12 @@
<zopeConfigure xmlns="http://namespaces.zope.org/zope">
-
+
<directives namespace="http://namespaces.zope.org/mail">
- <directive name="mailservice" handler=".metaconfigure.mailservice">
+ <directive name="queuedService" handler=".metaconfigure.queuedService">
<description>
- This directive creates and registers a global mail service. It should
- be only called once during startup.
+ This directive creates and registers a global queued mail service. It
+ should be only called once during startup.
</description>
<attribute name="name" required="no">
@@ -22,77 +22,88 @@
</description>
</attribute>
- <attribute name="class" required="yes">
+ <attribute name="queuePath" required="yes">
<description>
- Class of the Mail Service.
+ Defines the path for the queue directory.
</description>
</attribute>
- <attribute name="hostname" required="no">
- <description>
- Name of the server that is used to send the mail. Default is set to
- 'localhost'.
- </description>
- </attribute>
+ </directive>
- <attribute name="port" required="no">
+ <directive name="directService" handler=".metaconfigure.directService">
+
+ <description>
+ This directive creates and registers a global direct mail service. It
+ should be only called once during startup.
+ </description>
+
+ <attribute name="name" required="no">
<description>
- Port on the server that is used to send the mail. Default is set to
- to the standard port '25'.
+ Specifies the Service name of the mail service. The default is
+ "Mail".
</description>
</attribute>
- <attribute name="username" required="no">
+ <attribute name="permission" required="yes">
<description>
- Some SMTP servers support authentication. If no username is given,
- then the Mail Service will not try to use authentication.
+ Defines the permission that is required to use this object.
</description>
</attribute>
- <attribute name="password" required="no">
+ <attribute name="mailer" required="yes">
<description>
- Password that is used for authentication. Makes only sense in
- combination with username.
+ Defines the mailer to be used for sending mail.
</description>
</attribute>
</directive>
- <directive name="mailer" handler=".metaconfigure.mailer">
+ <!-- example of a mailer directive
+
+ <directive name="smtp" handler=".metaconfigure.smtp">
<description>
- Registers a new mailer class wiht the global translation service.
+ Registers a new SMTP mailer.
</description>
- <attribute name="name" required="yes">
+ <attribute name="id" required="yes">
+ <description>
+ ID of the mailer.
+ </description>
+ </attribute>
+
+ <attribute name="hostname" required="no">
<description>
- Name of the mailer class under which it is registered in the global
- mail service.
+ Name of the server that is used to send the mail. Default is set to
+ 'localhost'.
</description>
</attribute>
- <attribute name="class" required="yes">
+ <attribute name="port" required="no">
<description>
- The class representing this object.
+ Port on the server that is used to send the mail. Default is set to
+ to the standard port '25'.
</description>
</attribute>
- <attribute name="serviceType" required="no">
+ <attribute name="username" required="no">
<description>
- Specifies the service type for which the mailer should be
- registered. The default is "Mail".
+ Some SMTP servers support authentication. If no username is given,
+ then the Mail Service will not try to use authentication.
</description>
</attribute>
- <attribute name="default" required="no">
+ <attribute name="password" required="no">
<description>
- Specifies whether this mailer is the default mailer. The default
- value for the 'default' attribute is "False".
+ Password that is used for authentication. Makes only sense in
+ combination with username.
</description>
</attribute>
</directive>
+ -->
+
</directives>
-</zopeConfigure>
\ No newline at end of file
+</zopeConfigure>
=== Zope3/src/zope/app/mail/metaconfigure.py 1.2 => 1.2.2.1 ===
--- Zope3/src/zope/app/mail/metaconfigure.py:1.2 Mon May 19 06:03:37 2003
+++ Zope3/src/zope/app/mail/metaconfigure.py Thu May 22 04:51:40 2003
@@ -18,17 +18,15 @@
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
-def mailservice(_context, class_, permission, name="Mail",
- hostname="localhost", port=25, username=None, password=None):
-
- component = _context.resolve(class_)()
- component.hostname = hostname
- component.port = int(port)
- component.username = username
- component.password = password
+def queuedService(_context, permission, queuePath, 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)
return [
Action(
discriminator = ('service', name),
@@ -37,22 +35,42 @@
)
]
-
-def mailer(_context, name, class_, serviceType="Mail", default=False):
- klass = _context.resolve(class_)
-
- if default == "True":
- default = True
-
- def register(serviceType, name, klass, default):
- mailservice = getService(None, serviceType)
- mailservice.provideMailer(name, klass, default)
-
-
+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)
return [
Action(
- discriminator = ('mailer', name),
- callable = register,
- args = (serviceType, name, klass, default)
- )
+ discriminator = ('service', name),
+ callable = provideService,
+ args = (name, component, permission),
+ )
]
+
+# Example of mailer configuration:
+#
+# def smtp(_context, id, hostname, port):
+# component = SMTPMailer(hostname, port)
+# if queryMailer(id) is not None:
+# raise ConfigurationError("Redefinition of mailer %r" % id)
+# provideMailer(id, component)
+# return []
+#
+# or is it better to make mailer registration an Action? But that won't work,
+# because queryMailer will get called during directive processing, before any
+# actions are run.
+
+
+mailerRegistry = {}
+queryMailer = mailerRegistry.get
+provideMailer = mailerRegistry.__setitem__
+
+# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
+try:
+ from zope.testing.cleanup import addCleanUp
+except ImportError:
+ pass
+else:
+ addCleanUp(mailerRegistry.clear)
+ del addCleanUp
=== Zope3/src/zope/app/mail/service.py 1.2 => 1.2.2.1 ===
--- Zope3/src/zope/app/mail/service.py:1.2 Mon May 19 06:03:37 2003
+++ Zope3/src/zope/app/mail/service.py Thu May 22 04:51:40 2003
@@ -11,58 +11,96 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""MailService Implementation
-
-This module contains various implementations of MailServices.
+"""Mail service implementation
$Id$
"""
-from zope.app.interfaces.mail import IAsyncMailService
+import rfc822
+from cStringIO import StringIO
+from random import randrange
+from time import strftime
+from socket import gethostname
+from os import getpid
+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)
-class AsyncMailService:
- __doc__ = IAsyncMailService.__doc__
+ def __init__(self, queuePath):
+ self._queuePath = queuePath
- __implements__ = IAsyncMailService
+ queuePath = property(lambda self: self._queuePath)
- # 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
+ def createDataManager(self, fromaddr, toaddrs, message):
+ maildir = Maildir(self.queuePath, True)
+ msg = maildir.newMessage()
+ msg.write(message)
+ return MailDataManager(msg.commit, onAbort=msg.abort)
=== Removed File Zope3/src/zope/app/mail/mail.py ===
=== Removed File Zope3/src/zope/app/mail/mailer.py ===