~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/mail/protocols.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.mail.test.test_mail -*-
 
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""Protocol support for twisted.mail."""
 
7
 
 
8
# twisted imports
 
9
from twisted.mail import pop3
 
10
from twisted.mail import smtp
 
11
from twisted.internet import protocol
 
12
from twisted.internet import defer
 
13
from twisted.copyright import longversion
 
14
from twisted.python import log
 
15
 
 
16
from twisted import cred
 
17
import twisted.cred.error
 
18
import twisted.cred.credentials
 
19
 
 
20
from twisted.mail import relay
 
21
 
 
22
from zope.interface import implements
 
23
 
 
24
 
 
25
class DomainDeliveryBase:
 
26
    """A server that uses twisted.mail service's domains."""
 
27
 
 
28
    implements(smtp.IMessageDelivery)
 
29
    
 
30
    service = None
 
31
    protocolName = None
 
32
 
 
33
    def __init__(self, service, user, host=smtp.DNSNAME):
 
34
        self.service = service
 
35
        self.user = user
 
36
        self.host = host
 
37
    
 
38
    def receivedHeader(self, helo, origin, recipients):
 
39
        authStr = heloStr = ""
 
40
        if self.user:
 
41
            authStr = " auth=%s" % (self.user.encode('xtext'),)
 
42
        if helo[0]:
 
43
            heloStr = " helo=%s" % (helo[0],)
 
44
        from_ = "from %s ([%s]%s%s)" % (helo[0], helo[1], heloStr, authStr)
 
45
        by = "by %s with %s (%s)" % (
 
46
            self.host, self.protocolName, longversion
 
47
        )
 
48
        for_ = "for <%s>; %s" % (' '.join(map(str, recipients)), smtp.rfc822date())
 
49
        return "Received: %s\n\t%s\n\t%s" % (from_, by, for_)
 
50
    
 
51
    def validateTo(self, user):
 
52
        # XXX - Yick.  This needs cleaning up.
 
53
        if self.user and self.service.queue:
 
54
            d = self.service.domains.get(user.dest.domain, None)
 
55
            if d is None:
 
56
                d = relay.DomainQueuer(self.service, True)
 
57
        else:
 
58
            d = self.service.domains[user.dest.domain]
 
59
        return defer.maybeDeferred(d.exists, user)
 
60
 
 
61
    def validateFrom(self, helo, origin):
 
62
        if not helo:
 
63
            raise smtp.SMTPBadSender(origin, 503, "Who are you?  Say HELO first.")
 
64
        if origin.local != '' and origin.domain == '':
 
65
            raise smtp.SMTPBadSender(origin, 501, "Sender address must contain domain.")
 
66
        return origin
 
67
 
 
68
    def startMessage(self, users):
 
69
        ret = []
 
70
        for user in users:
 
71
            ret.append(self.service.domains[user.dest.domain].startMessage(user))
 
72
        return ret
 
73
 
 
74
 
 
75
class SMTPDomainDelivery(DomainDeliveryBase):
 
76
    protocolName = 'smtp'
 
77
 
 
78
class ESMTPDomainDelivery(DomainDeliveryBase):
 
79
    protocolName = 'esmtp'
 
80
 
 
81
class DomainSMTP(SMTPDomainDelivery, smtp.SMTP):
 
82
    service = user = None
 
83
 
 
84
    def __init__(self, *args, **kw):
 
85
        import warnings
 
86
        warnings.warn(
 
87
            "DomainSMTP is deprecated.  Use IMessageDelivery objects instead.",
 
88
            DeprecationWarning, stacklevel=2,
 
89
        )
 
90
        smtp.SMTP.__init__(self, *args, **kw)
 
91
        if self.delivery is None:
 
92
            self.delivery = self
 
93
 
 
94
class DomainESMTP(ESMTPDomainDelivery, smtp.ESMTP):
 
95
    service = user = None
 
96
 
 
97
    def __init__(self, *args, **kw):
 
98
        import warnings
 
99
        warnings.warn(
 
100
            "DomainESMTP is deprecated.  Use IMessageDelivery objects instead.",
 
101
            DeprecationWarning, stacklevel=2,
 
102
        )
 
103
        smtp.ESMTP.__init__(self, *args, **kw)
 
104
        if self.delivery is None:
 
105
            self.delivery = self
 
106
 
 
107
class SMTPFactory(smtp.SMTPFactory):
 
108
    """A protocol factory for SMTP."""
 
109
 
 
110
    protocol = smtp.SMTP
 
111
    portal = None
 
112
 
 
113
    def __init__(self, service, portal = None):
 
114
        smtp.SMTPFactory.__init__(self)
 
115
        self.service = service
 
116
        self.portal = portal
 
117
    
 
118
    def buildProtocol(self, addr):
 
119
        log.msg('Connection from %s' % (addr,))
 
120
        p = smtp.SMTPFactory.buildProtocol(self, addr)
 
121
        p.service = self.service
 
122
        p.portal = self.portal
 
123
        return p
 
124
 
 
125
class ESMTPFactory(SMTPFactory):
 
126
    protocol = smtp.ESMTP
 
127
    context = None
 
128
 
 
129
    def __init__(self, *args):
 
130
        SMTPFactory.__init__(self, *args)
 
131
        self.challengers = {
 
132
            'CRAM-MD5': cred.credentials.CramMD5Credentials
 
133
        }
 
134
    
 
135
    def buildProtocol(self, addr):
 
136
        p = SMTPFactory.buildProtocol(self, addr)
 
137
        p.challengers = self.challengers
 
138
        p.ctx = self.context
 
139
        return p
 
140
 
 
141
class VirtualPOP3(pop3.POP3):
 
142
    """Virtual hosting POP3."""
 
143
 
 
144
    service = None
 
145
 
 
146
    domainSpecifier = '@' # Gaagh! I hate POP3. No standardized way
 
147
                          # to indicate user@host. '@' doesn't work
 
148
                          # with NS, e.g.
 
149
 
 
150
    def authenticateUserAPOP(self, user, digest):
 
151
        # Override the default lookup scheme to allow virtual domains
 
152
        user, domain = self.lookupDomain(user)
 
153
        try:
 
154
            portal = self.service.lookupPortal(domain)
 
155
        except KeyError:
 
156
            return defer.fail(cred.error.UnauthorizedLogin())
 
157
        else:
 
158
            return portal.login(
 
159
                pop3.APOPCredentials(self.magic, user, digest),
 
160
                None,
 
161
                pop3.IMailbox
 
162
            )
 
163
 
 
164
    def authenticateUserPASS(self, user, password):
 
165
        user, domain = self.lookupDomain(user)
 
166
        try:
 
167
            portal = self.service.lookupPortal(domain)
 
168
        except KeyError:
 
169
            return defer.fail(cred.error.UnauthorizedLogin())
 
170
        else:
 
171
            return portal.login(
 
172
                cred.credentials.UsernamePassword(user, password),
 
173
                None,
 
174
                pop3.IMailbox
 
175
            )
 
176
 
 
177
    def lookupDomain(self, user):
 
178
        try:
 
179
            user, domain = user.split(self.domainSpecifier, 1)
 
180
        except ValueError:
 
181
            domain = ''
 
182
        if domain not in self.service.domains:
 
183
             raise pop3.POP3Error("no such domain %s" % domain)
 
184
        return user, domain
 
185
 
 
186
 
 
187
class POP3Factory(protocol.ServerFactory):
 
188
    """POP3 protocol factory."""
 
189
 
 
190
    protocol = VirtualPOP3
 
191
    service = None
 
192
 
 
193
    def __init__(self, service):
 
194
        self.service = service
 
195
    
 
196
    def buildProtocol(self, addr):
 
197
        p = protocol.ServerFactory.buildProtocol(self, addr)
 
198
        p.service = self.service
 
199
        return p
 
200
 
 
201
#
 
202
# It is useful to know, perhaps, that the required file for this to work can
 
203
# be created thusly:
 
204
#
 
205
# openssl req -x509 -newkey rsa:2048 -keyout file.key -out file.crt \
 
206
# -days 365 -nodes
 
207
#
 
208
# And then cat file.key and file.crt together.  The number of days and bits
 
209
# can be changed, of course.
 
210
#
 
211
class SSLContextFactory:
 
212
    """An SSL Context Factory
 
213
    
 
214
    This loads a certificate and private key from a specified file.
 
215
    """
 
216
    def __init__(self, filename):
 
217
        self.filename = filename
 
218
 
 
219
    def getContext(self):
 
220
        """Create an SSL context."""
 
221
        from OpenSSL import SSL
 
222
        ctx = SSL.Context(SSL.SSLv23_METHOD)
 
223
        ctx.use_certificate_file(self.filename)
 
224
        ctx.use_privatekey_file(self.filename)
 
225
        return ctx