Index: main/waeup.sirp/trunk/buildout.cfg
===================================================================
--- main/waeup.sirp/trunk/buildout.cfg	(revision 7469)
+++ main/waeup.sirp/trunk/buildout.cfg	(revision 7470)
@@ -84,4 +84,7 @@
     ${zope_conf:logfiles}
     ${zope_conf:blobstorage}
+    ${buildout:directory}/var/mailqueue/cur
+    ${buildout:directory}/var/mailqueue/tmp
+    ${buildout:directory}/var/mailqueue/new
 
 [test]
@@ -126,4 +129,7 @@
 
 [log]
+recipe = zc.recipe.filestorage
+
+[mailqueue]
 recipe = zc.recipe.filestorage
 
Index: main/waeup.sirp/trunk/setup.py
===================================================================
--- main/waeup.sirp/trunk/setup.py	(revision 7469)
+++ main/waeup.sirp/trunk/setup.py	(revision 7470)
@@ -6,5 +6,5 @@
 install_requires =[
     'setuptools',
-    'gp.fileupload',
+    #'gp.fileupload',
     'grok',
     'grokcore.startup',
@@ -34,4 +34,5 @@
     'zope.errorview',
     'zope.schema >= 3.8.0',
+    'zope.sendmail',
     ],
 
Index: main/waeup.sirp/trunk/src/waeup/sirp/configure.zcml
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/configure.zcml	(revision 7469)
+++ main/waeup.sirp/trunk/src/waeup/sirp/configure.zcml	(revision 7470)
@@ -5,4 +5,6 @@
   <includeDependencies package="." />
   <grok:grok package="." />
+
+  <include package="." file="mail.zcml" />
   <includeOverrides package="waeup.sirp.utils" file="overrides.zcml" />
 </configure>
Index: main/waeup.sirp/trunk/src/waeup/sirp/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/interfaces.py	(revision 7469)
+++ main/waeup.sirp/trunk/src/waeup/sirp/interfaces.py	(revision 7470)
@@ -422,4 +422,11 @@
         )
 
+    smtp_mailer = schema.Choice(
+        title = u'SMTP mailer to use when sending mail',
+        vocabulary = 'Mail Delivery Names',
+        default = 'No email service',
+        required = True,
+        )
+
     smtp_server = schema.TextLine(
         title = u'Adress of SMTP Server',
Index: main/waeup.sirp/trunk/src/waeup/sirp/mail.zcml
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/mail.zcml	(revision 7470)
+++ main/waeup.sirp/trunk/src/waeup/sirp/mail.zcml	(revision 7470)
@@ -0,0 +1,82 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:mail="http://namespaces.zope.org/mail">
+  <!--
+
+   SMTP Settings
+   *************
+
+   In here we define the mailers and mail deliveries we want to be
+   used by the portal.
+
+   They are set here (and not dynamically in the portal) to increase
+   security. Even portal users with all permissions cannot change mail
+   server credentials and related. You need filesystem access to
+   change/override the settings in here.
+
+   In the portal you can pick one of the deliveries for use with all
+   SMTP (aka mail) related functionality.
+
+   Each delivery (direct or queued) is bound to a certain mailer,
+   defined by a 'mail:smtpMailer' directive.
+
+   The smtpMailer directives define real mail hosts (local or remote)
+   where mail from the portal should be processed.
+
+   The 'mail:directDelivery' and 'mail:queuedDelivery' directives send
+   local mail to the mailer set in their 'mailer' attribute. While
+   directDelivery sends mail immediately when a request is finished
+   (see zope.sendmail for more info), queuedDelivery handles mail in a
+   background thread and sends mail independently from requests.
+
+   By default in SIRP we have a fake mailer activated, that does not
+   send real mail to the outside world.
+  -->
+
+  <!-- A mail server on localhost -->
+  <!-- Mail server definitions require the following attributes:
+
+        - name: (string) name to be used by deliveries
+
+       Mail server definitions allow the following additional attribs:
+
+        - hostname   (string) hostname or IP(?) of mail server to use
+                              ('localhost' by default)
+        - port       (int number) port to connect to SMTP mailer
+                              (25 by default)
+        - username   (string) Username to log in to mail server
+        - password   (string) Password to log in to mail server
+
+  -->
+  <mail:smtpMailer
+      name="smtp.localhost"
+      hostname="localhost"
+      port="25"
+      />
+
+  <!-- The mail deliveries -->
+  <!-- Mail deliveries require the following attributes:
+
+        - name:       (string) this string will appear in portal select
+                               boxes
+        - permission: (string) zope default perms only
+        - mailer:     (string) name of some mailer registered above or
+                               'smtp'
+        - queuePath:  (string) must exist and contain cur/, new/, and
+                               tmp/ dirs. Relative paths are relative
+                               to the directory this files lives in.
+
+   -->
+  <mail:queuedDelivery
+      name="Queued SMTP on localhost"
+      permission="zope.Public"
+      mailer="smtp.localhost"
+      queuePath="../../../var/mailqueue"
+      />
+
+  <mail:directDelivery
+      name="Direct SMTP on localhost"
+      permission="zope.Public"
+      mailer="smtp.localhost"
+      />
+
+</configure>
Index: main/waeup.sirp/trunk/src/waeup/sirp/smtp.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/smtp.py	(revision 7470)
+++ main/waeup.sirp/trunk/src/waeup/sirp/smtp.py	(revision 7470)
@@ -0,0 +1,167 @@
+##
+## smtp.py
+## Login : <uli@pu.smp.net>
+## Started on  Mon Dec 19 21:30:23 2011 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2011 Uli Fouquet
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Email (SMTP) services for SIRP.
+
+Note About Encodings
+--------------------
+
+All functions in this module expect any raw strings passed in to be
+encoded 'utf-8' (if you pass in unicode objects instead, this is not a
+problem).
+
+This is because we cannot easily tell from a raw string (it is in fact
+only a byte stream) what encoding it has. In latin-1 and utf-8, for
+instance, there exist some chars (byte values) that have different
+meanings in both encodings. If we see such a char in a byte stream:
+what is it meant to represent? The respective character from latin1 or
+the one from utf-8?
+
+We therefore interpret all internally used raw strings to be encoded as
+utf-8.
+
+The functions in here nevertheless try hard to produce output (mail
+parts, headers, etc.) encoded in the least complex manner. For
+instance if you pass in some address or mail body that is
+representable (correctly) as ASCII or latin-1, we will turn the text
+into that encoding (given, you passed it in as utf-8) to stay as
+compatible as possible with old mailers that do not understand utf-8.
+
+"""
+import grok
+import logging
+import smtplib
+from email.Header import Header
+from email.Utils import parseaddr, formataddr
+from email.mime.text import MIMEText
+from zope.component import getUtility
+from zope.interface import Interface
+from zope.sendmail.interfaces import IMailDelivery
+
+
+# XXX: Move to interfaces.py
+class IMailService(Interface):
+
+    def __call__():
+        """Get the default mail delivery.
+        """
+
+class DefaultMailService(grok.GlobalUtility):
+    """Returns a :class:`zope.sendmail.IMailDelivery`.
+
+    Searches a site from current request (if applicable) and returns
+    the mail delivery set for this site or a fake mailer that does not
+    really send mail (for testing, evaluating, etc.).
+    """
+    grok.implements(IMailService)
+
+    def __call__(self):
+        name = 'No email service'
+        site = grok.getSite()
+        if site is not None:
+            config = site['configuration']
+            name = getattr(config, 'smtp_mailer', name)
+        return getUtility(IMailDelivery, name=name)
+
+class FakeSMTPDelivery(grok.GlobalUtility):
+    """A fake mail delivery for testing etc.
+
+    Instead of sending real mails, this mailer only logs received
+    messages to the ``test.smtp`` logger.
+    """
+    grok.implements(IMailDelivery)
+    grok.name('No email service')
+
+    def send(self, fromaddr, toaddrs, message):
+        logger = logging.getLogger('test.smtp')
+        rcpts = ', '.join([x.decode('utf-8') for x in toaddrs])
+        logger.info(
+            u"Sending email from %s to %s:" % (
+                fromaddr.decode('utf-8'), rcpts))
+        logger.info(u"Message:")
+        for line in message.split('\n'):
+            logger.info(u"msg: " + line.decode('utf-8'))
+        return 'fake-message-id@example.com'
+
+CHARSETS = ('US-ASCII', 'ISO-8859-1', 'UTF-8')
+
+def encode_header_item(item):
+    if not isinstance(item, unicode):
+        item = unicode(item, 'utf-8')
+    return str(Header(item, 'latin1')) # try ascii, then latin1, then utf-8
+
+def encode_address(addr, name=u''):
+    """Turn email address parts into a single valid email string.
+
+    The given email address and the name are turned into a single
+    (byte stream) string, suitable for use with ``To:`` or ``From:``
+    headers in emails.
+
+    Any encodings to a mailer-readable format are performed.
+
+    Preferred input format is unicode, although also raw strings (byte
+    streams) work as long as they are decodable from UTF-8.
+
+    That means: if you pass in non-unicode string, take care to
+    deliver utf-8 encoded strings (or plain ASCII).
+
+    Returns a single (raw) string like "My Name <my@sample.com>".
+    """
+    addr = encode_header_item(addr)
+    name = encode_header_item(name)
+    return formataddr((name, addr))
+
+def encode_body(text):
+    """Build MIME message part from text.
+
+    You can pass unicode objects or simple strings as text.
+
+    .. warn:: If the input is a simple string, this string is expected
+              to be encoded 'utf-8'!
+
+    Returns a MIMEText object.
+    """
+    if not isinstance(text, unicode):
+        text = unicode(text, 'utf-8')
+    charset = CHARSETS[-1] # fallback
+    for charset in CHARSETS:
+        try:
+            text = text.encode(charset)
+        except UnicodeError:
+            pass # try next encoding
+        else:
+            break
+    return MIMEText(text, 'plain', charset)
+
+def send_mail(from_name, from_addr, rcpt_name, rcpt_addr,
+              subject, body, config=None):
+    """Send mail.
+    """
+    # format message
+    body = encode_body(body)
+    body["From"] = encode_address(from_addr, from_name)
+    body["To"] = encode_address(rcpt_addr, rcpt_name)
+    body["Subject"] = encode_header_item(subject)
+
+    mailer = getUtility(IMailService)
+    result = mailer().send(from_addr, [rcpt_addr], body.as_string())
+    return result
Index: main/waeup.sirp/trunk/src/waeup/sirp/tests/test_smtp.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/tests/test_smtp.py	(revision 7470)
+++ main/waeup.sirp/trunk/src/waeup/sirp/tests/test_smtp.py	(revision 7470)
@@ -0,0 +1,356 @@
+# -*- coding: utf-8 -*-
+##
+## test_smtp.py
+## Login : <uli@pu.smp.net>
+## Started on  Wed Dec 21 15:08:50 2011 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2011 Uli Fouquet
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+# Tests for email-related components
+import base64
+import logging
+import tempfile
+import shutil
+import unittest
+from StringIO import StringIO
+from zope.component import getUtility
+from zope.component.hooks import setSite, clearSite
+from zope.sendmail.interfaces import IMailDelivery
+from waeup.sirp.app import University
+from waeup.sirp.smtp import (
+    encode_header_item, encode_address, encode_body, FakeSMTPDelivery,
+    IMailService, send_mail)
+from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
+
+#
+# SMTP test config
+#
+
+# Also run tests that send mail to external servers?
+#   If you enable this, please make sure the external smtp hosts and receivers
+#   do exist really and are not bothered by being spammed by a test programme.
+EXTERNAL_MAIL_TESTS = False
+
+# Maybe existing receiver of externally sent mail. If this mail account
+# exists really, it might receive mail from the tests!
+EXTERNAL_MAIL_RECEIVER = 'no-user@waeup.org'
+
+# Names of mail deliveries to use when external mail tests are enabled.
+#   See local mail.zcml for names of available deliveries.
+EXTERNAL_DIRECT_DELIVERY = 'Direct SMTP on localhost'
+EXTERNAL_QUEUED_DELIVERY = 'Queued SMTP on localhost'
+
+#
+# end of SMTP test config
+#
+
+def external_mail_test(func):
+    if not EXTERNAL_MAIL_TESTS:
+        myself = __file__
+        if myself.endswith('.pyc'):
+            myself = myself[:-2]
+        print "WARNING: external mail tests are skipped!"
+        print "WARNING: edit %s to enable them." % myself
+        return
+    return func
+
+class HelperTests(unittest.TestCase):
+
+    def test_encode_header_item(self):
+        # encoding header items works with strings and unicodes, as
+        # long as we pass in utf-8 encoded stuff.
+        result1 = encode_header_item(u'Plain Name'.encode('utf-8'))
+        result2 = encode_header_item(u'Name with umläut'.encode('utf-8'))
+        result3 = encode_header_item(u'Plain Name')
+        result4 = encode_header_item(u'Name with umläut')
+        self.assertEqual(result1, u'Plain Name')
+        self.assertEqual(result2, u'=?latin1?q?Name_with_uml=E4ut?=')
+        self.assertEqual(result3, u'Plain Name')
+        self.assertEqual(result4, u'=?latin1?q?Name_with_uml=E4ut?=')
+        return
+
+    def test_encode_address(self):
+        # we can correctly encode address parts
+        result1 = encode_address('foo@bar.baz')
+        result2 = encode_address(u'foo@bar.baz', 'The Foo' )
+        result3 = encode_address('foo@bar.baz', u'The Foo')
+        result4 = encode_address('foo@bar.baz', u'With Umläut')
+        self.assertEqual(result1, 'foo@bar.baz')
+        self.assertEqual(result2, 'The Foo <foo@bar.baz>')
+        self.assertEqual(result3, 'The Foo <foo@bar.baz>')
+        self.assertEqual(result4, '=?latin1?q?With_Uml=E4ut?= <foo@bar.baz>')
+        return
+
+    def test_encode_body(self):
+        result1 = encode_body(u'Simple Text')
+        self.assertEqual(
+            str(result1).split('\n')[1:],
+            ['MIME-Version: 1.0',
+             'Content-Type: text/plain; charset="us-ascii"',
+             'Content-Transfer-Encoding: 7bit',
+             '',
+             'Simple Text'])
+        return
+
+    def test_encode_body_latin1_string(self):
+        # utf-8 encoded byte streams are transferred correctly when
+        # they contain chars unrepresentable in ascii but available in latin1
+        text = u'Simple text with Ümläut'.encode('utf-8')
+        result1 = encode_body(text)
+        result1 = str(result1).split('\n')[1:]
+        self.assertEqual(
+            result1,
+            ['MIME-Version: 1.0',
+             'Content-Type: text/plain; charset="iso-8859-1"',
+             'Content-Transfer-Encoding: quoted-printable',
+             '',
+             'Simple text with =DCml=E4ut'])
+        return
+
+    def test_encode_body_latin1_unicode(self):
+        # unicode strings are transferred correctly when they contain
+        # chars unrepresentable in ascii but available in latin1
+        text = u'Simple text with Ümläut'
+        result1 = encode_body(text)
+        result1 = str(result1).split('\n')[1:]
+        self.assertEqual(
+            result1,
+            ['MIME-Version: 1.0',
+             'Content-Type: text/plain; charset="iso-8859-1"',
+             'Content-Transfer-Encoding: quoted-printable',
+             '',
+             'Simple text with =DCml=E4ut'])
+        return
+
+    def test_encode_body_utf8_string(self):
+        # utf-8 encoded byte streams are transferred correctly when
+        # they contain chars unrepresentable in ascii but available in latin1
+        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
+        text = text.encode('utf-8') # turn unicode into byte stream
+        result1 = encode_body(text)
+        result1 = str(result1).split('\n')[1:]
+        self.assertEqual(
+            result1,
+            ['MIME-Version: 1.0',
+             'Content-Type: text/plain; charset="utf-8"',
+             'Content-Transfer-Encoding: base64',
+             '',
+             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
+        self.assertEqual(
+            base64.b64decode(result1[-2]).decode('utf-8'),
+            u"Simple text with ü and capital pi: " + unichr(0x3a0))
+        return
+
+    def test_encode_body_utf8_unicode(self):
+        # utf-8 encoded byte streams are transferred correctly when
+        # they contain chars unrepresentable in latin1
+        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
+        result1 = encode_body(text)
+        result1 = str(result1).split('\n')[1:]
+        self.assertEqual(
+            result1,
+            ['MIME-Version: 1.0',
+             'Content-Type: text/plain; charset="utf-8"',
+             'Content-Transfer-Encoding: base64',
+             '',
+             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
+        self.assertEqual(
+            base64.b64decode(result1[-2]).decode('utf-8'),
+            u"Simple text with ü and capital pi: " + unichr(0x3a0))
+        return
+
+class FunctionalMailerTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(FunctionalMailerTests, self).setUp()
+        self.setup_logging()
+        return
+
+    def tearDown(self):
+        super(FunctionalMailerTests, self).tearDown()
+        self.teardown_logging()
+        return
+
+    def setup_logging(self):
+        # setup a log-handler that catches all fake mailer output
+        self.stream = StringIO()
+        handler = logging.StreamHandler(self.stream)
+        logger = logging.getLogger('test.smtp')
+        logger.addHandler(handler)
+        logger.setLevel(logging.INFO)
+        return
+
+    def get_fake_smtp_output(self):
+        # get output generated by fake mailer
+        self.stream.flush()
+        self.stream.seek(0)
+        return self.stream.read()
+
+    def teardown_logging(self):
+        # remove the log handler for fake mailer output
+        logger = logging.getLogger('test.smtp')
+        handlers = [x for x in logger.handlers]
+        for handler in handlers:
+            logger.removeHandler(handler)
+        return
+
+    def test_get_fake_mailer(self):
+        # we can get the fake mailer if we want
+        mailer = getUtility(IMailDelivery, name='No email service')
+        self.assertTrue(isinstance(mailer, FakeSMTPDelivery))
+        return
+
+    def test_get_default_mailer(self):
+        # we can get a default mailer if we want
+        mailer = getUtility(IMailService)
+        self.assertTrue(isinstance(mailer(), FakeSMTPDelivery))
+        return
+
+    def test_send_mail(self):
+        # we can really send mail.
+        mail_id = send_mail(
+            u'A sender', u'sender@example.com',
+            u'A recipient', u'recpt@example.com',
+            u'A subject',
+            u'This is a test mail.')
+        self.assertEqual(mail_id, 'fake-message-id@example.com')
+        self.assertEqual(
+            self.get_fake_smtp_output().split('\n'),
+            [u'Sending email from sender@example.com to recpt@example.com:',
+             u'Message:',
+             u'msg: MIME-Version: 1.0',
+             u'msg: Content-Type: text/plain; charset="us-ascii"',
+             u'msg: Content-Transfer-Encoding: 7bit',
+             u'msg: From: A sender <sender@example.com>',
+             u'msg: To: A recipient <recpt@example.com>',
+             u'msg: Subject: A subject',
+             u'msg: ',
+             u'msg: This is a test mail.',
+             u'']
+            )
+        return
+
+    def test_send_mail_utf8_strings(self):
+        # we can send mail with utf-8 encoded strings as input
+        mail_id = send_mail(
+            u'A sender', u'sender@example.com',
+            u'A recipient', u'recpt@example.com',
+            u'A subject',
+            u'This is a test mail with ümläut.'.encode('utf-8'))
+        self.assertEqual(mail_id, 'fake-message-id@example.com')
+        self.assertEqual(
+            self.get_fake_smtp_output().split('\n'),
+            [u'Sending email from sender@example.com to recpt@example.com:',
+             u'Message:', u'msg: MIME-Version: 1.0',
+             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
+             u'msg: Content-Transfer-Encoding: quoted-printable',
+             u'msg: From: A sender <sender@example.com>',
+             u'msg: To: A recipient <recpt@example.com>',
+             u'msg: Subject: A subject',
+             u'msg: ',
+             u'msg: This is a test mail with =FCml=E4ut.',
+             u'']
+            )
+        return
+
+    def test_send_mail_utf8_unicode(self):
+        # we can send mail with utf-8 encoded unicode as input
+        mail_id = send_mail(
+            u'A sender', u'sender@example.com',
+            u'A recipient', u'recpt@example.com',
+            u'A subject',
+            u'This is a test mail with ümläut.')
+        self.assertEqual(mail_id, 'fake-message-id@example.com')
+        self.assertEqual(
+            self.get_fake_smtp_output().split('\n'),
+            [u'Sending email from sender@example.com to recpt@example.com:',
+             u'Message:', u'msg: MIME-Version: 1.0',
+             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
+             u'msg: Content-Transfer-Encoding: quoted-printable',
+             u'msg: From: A sender <sender@example.com>',
+             u'msg: To: A recipient <recpt@example.com>',
+             u'msg: Subject: A subject',
+             u'msg: ',
+             u'msg: This is a test mail with =FCml=E4ut.',
+             u'']
+            )
+        return
+
+
+class ExternalMailerTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ExternalMailerTests, self).setUp()
+        # Setup a sample site for each test
+        app = University()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Prepopulate the ZODB...
+        self.getRootFolder()['app'] = app
+        self.app = self.getRootFolder()['app']
+        return
+
+    def tearDown(self):
+        super(ExternalMailerTests, self).tearDown()
+        shutil.rmtree(self.dc_root)
+        return
+
+    def test_config_default_mailer(self):
+        # The default mailer set in config is 'no mailer'.
+        self.assertEqual(
+            getattr(self.app.get('configuration', None), 'smtp_mailer'),
+            u'No email service')
+        return
+
+    @external_mail_test
+    def test_send_direct_mail(self):
+        # send mail using direct mail delivery
+        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
+        setSite(self.app)
+        result = send_mail(
+            'test program', 'no-reply@waeup.org',
+            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
+            'Test Mail from WAeUP SIRP',
+            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
+            )
+        import transaction
+        transaction.commit() # The mail is really sent when transactions is
+                             # committed
+        self.assertEqual(result, 'asd')
+        return
+
+    @external_mail_test
+    def test_send_queued_mail(self):
+        # send mail using queued mail delivery
+        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
+        setSite(self.app)
+        result = send_mail(
+            'test program', 'no-reply@waeup.org',
+            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
+            'Test Mail from WAeUP SIRP',
+            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
+            )
+        import transaction
+        transaction.commit() # The mail is really sent when transactions is
+                             # committed
+        self.assertEqual(result, 'asd')
+        return
