~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""An IMailer that stores messages in a specified mbox file."""


__metaclass__ = type


import email
import mailbox

from email.Utils import make_msgid
from logging import getLogger

from zope.app import zapi
from zope.sendmail.interfaces import IMailer
from zope.interface import implements

COMMASPACE = ', '


class MboxMailer:
    """
    Stores the message in a Unix mailbox file.  This will be so much cooler
    when we can use Python 2.5's mailbox module.
    """
    implements(IMailer)

    def __init__(self, filename, overwrite, mailer=None):
        self.filename = filename
        if overwrite:
            # Truncate existing file.  Subsequent opens will always append so
            # this is effectively an overwrite.  Note that because IMailer
            # doesn't have a close() method, we can't leave the file open
            # here, otherwise it will never get closed.
            mbox_file = open(self.filename, 'w')
            mbox_file.close()
        self.mailer = mailer

    def send(self, fromaddr, toaddrs, message):
        """See IMailer."""
        env_recips = COMMASPACE.join(toaddrs)
        log = getLogger('canonical.launchpad.mail')
        log.info('Email from %s to %s being stored in mailbox %s',
                 fromaddr, env_recips, self.filename)
        msg = email.message_from_string(message)
        # Mimic what MTAs such as Postfix do in transfering the envelope
        # sender into the Return-Path header.  It's okay if the message has
        # multiple such headers.
        msg['Return-Path'] = fromaddr
        # Because it might be useful, copy the envelope recipients into the
        # RFC 2822 headers too.
        msg['X-Envelope-To'] = env_recips
        # Add the Message-ID required by the interface; even though the
        # interface says that the message text doesn't include such a header,
        # zap it first just in case.
        del msg['message-id']
        msg['Message-ID'] = message_id = make_msgid()
        mbox_file = open(self.filename, 'a')
        try:
            print >> mbox_file, msg
        finally:
            mbox_file.close()
        if self.mailer is not None:
            # Forward the message on to the chained mailer, if there is one.
            # This allows for example, the mboxMailer to be used in the test
            # suite, which requires that the testMailer eventually gets
            # called.
            chained_mailer = zapi.getUtility(IMailer, self.mailer)
            chained_mailer.send(fromaddr, toaddrs, message)
        return message_id