~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/mail/mail.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-2007 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""Mail support for twisted python.
 
7
"""
 
8
 
 
9
# Twisted imports
 
10
from twisted.internet import defer
 
11
from twisted.application import service, internet
 
12
from twisted.python import util
 
13
from twisted.python import log
 
14
 
 
15
from twisted import cred
 
16
import twisted.cred.portal
 
17
 
 
18
# Sibling imports
 
19
from twisted.mail import protocols, smtp
 
20
 
 
21
# System imports
 
22
import os
 
23
from zope.interface import implements, Interface
 
24
 
 
25
 
 
26
class DomainWithDefaultDict:
 
27
    '''Simulate a dictionary with a default value for non-existing keys.
 
28
    '''
 
29
    def __init__(self, domains, default):
 
30
        self.domains = domains
 
31
        self.default = default
 
32
 
 
33
    def setDefaultDomain(self, domain):
 
34
        self.default = domain
 
35
 
 
36
    def has_key(self, name):
 
37
        return 1
 
38
 
 
39
    def fromkeys(klass, keys, value=None):
 
40
        d = klass()
 
41
        for k in keys:
 
42
            d[k] = value
 
43
        return d
 
44
    fromkeys = classmethod(fromkeys)
 
45
 
 
46
    def __contains__(self, name):
 
47
        return 1
 
48
 
 
49
    def __getitem__(self, name):
 
50
        return self.domains.get(name, self.default)
 
51
 
 
52
    def __setitem__(self, name, value):
 
53
        self.domains[name] = value
 
54
 
 
55
    def __delitem__(self, name):
 
56
        del self.domains[name]
 
57
 
 
58
    def __iter__(self):
 
59
        return iter(self.domains)
 
60
 
 
61
    def __len__(self):
 
62
        return len(self.domains)
 
63
 
 
64
 
 
65
    def __str__(self):
 
66
        """
 
67
        Return a string describing the underlying domain mapping of this
 
68
        object.
 
69
        """
 
70
        return '<DomainWithDefaultDict %s>' % (self.domains,)
 
71
 
 
72
 
 
73
    def __repr__(self):
 
74
        """
 
75
        Return a pseudo-executable string describing the underlying domain
 
76
        mapping of this object.
 
77
        """
 
78
        return 'DomainWithDefaultDict(%s)' % (self.domains,)
 
79
 
 
80
 
 
81
    def get(self, key, default=None):
 
82
        return self.domains.get(key, default)
 
83
 
 
84
    def copy(self):
 
85
        return DomainWithDefaultDict(self.domains.copy(), self.default)
 
86
 
 
87
    def iteritems(self):
 
88
        return self.domains.iteritems()
 
89
 
 
90
    def iterkeys(self):
 
91
        return self.domains.iterkeys()
 
92
 
 
93
    def itervalues(self):
 
94
        return self.domains.itervalues()
 
95
 
 
96
    def keys(self):
 
97
        return self.domains.keys()
 
98
 
 
99
    def values(self):
 
100
        return self.domains.values()
 
101
 
 
102
    def items(self):
 
103
        return self.domains.items()
 
104
 
 
105
    def popitem(self):
 
106
        return self.domains.popitem()
 
107
 
 
108
    def update(self, other):
 
109
        return self.domains.update(other)
 
110
 
 
111
    def clear(self):
 
112
        return self.domains.clear()
 
113
 
 
114
    def setdefault(self, key, default):
 
115
        return self.domains.setdefault(key, default)
 
116
 
 
117
class IDomain(Interface):
 
118
    """An email domain."""
 
119
 
 
120
    def exists(user):
 
121
        """
 
122
        Check whether or not the specified user exists in this domain.
 
123
 
 
124
        @type user: C{twisted.protocols.smtp.User}
 
125
        @param user: The user to check
 
126
 
 
127
        @rtype: No-argument callable
 
128
        @return: A C{Deferred} which becomes, or a callable which
 
129
        takes no arguments and returns an object implementing C{IMessage}.
 
130
        This will be called and the returned object used to deliver the
 
131
        message when it arrives.
 
132
 
 
133
        @raise twisted.protocols.smtp.SMTPBadRcpt: Raised if the given
 
134
        user does not exist in this domain.
 
135
        """
 
136
 
 
137
    def addUser(user, password):
 
138
        """Add a username/password to this domain."""
 
139
 
 
140
    def startMessage(user):
 
141
        """Create and return a new message to be delivered to the given user.
 
142
 
 
143
        DEPRECATED.  Implement validateTo() correctly instead.
 
144
        """
 
145
 
 
146
    def getCredentialsCheckers():
 
147
        """Return a list of ICredentialsChecker implementors for this domain.
 
148
        """
 
149
 
 
150
class IAliasableDomain(IDomain):
 
151
    def setAliasGroup(aliases):
 
152
        """Set the group of defined aliases for this domain
 
153
 
 
154
        @type aliases: C{dict}
 
155
        @param aliases: Mapping of domain names to objects implementing
 
156
        C{IAlias}
 
157
        """
 
158
 
 
159
    def exists(user, memo=None):
 
160
        """
 
161
        Check whether or not the specified user exists in this domain.
 
162
 
 
163
        @type user: C{twisted.protocols.smtp.User}
 
164
        @param user: The user to check
 
165
 
 
166
        @type memo: C{dict}
 
167
        @param memo: A record of the addresses already considered while
 
168
        resolving aliases.  The default value should be used by all
 
169
        external code.
 
170
 
 
171
        @rtype: No-argument callable
 
172
        @return: A C{Deferred} which becomes, or a callable which
 
173
        takes no arguments and returns an object implementing C{IMessage}.
 
174
        This will be called and the returned object used to deliver the
 
175
        message when it arrives.
 
176
 
 
177
        @raise twisted.protocols.smtp.SMTPBadRcpt: Raised if the given
 
178
        user does not exist in this domain.
 
179
        """
 
180
 
 
181
class BounceDomain:
 
182
    """A domain in which no user exists.
 
183
 
 
184
    This can be used to block off certain domains.
 
185
    """
 
186
 
 
187
    implements(IDomain)
 
188
 
 
189
    def exists(self, user):
 
190
        raise smtp.SMTPBadRcpt(user)
 
191
 
 
192
    def willRelay(self, user, protocol):
 
193
        return False
 
194
 
 
195
    def addUser(self, user, password):
 
196
        pass
 
197
 
 
198
    def startMessage(self, user):
 
199
        """
 
200
        No code should ever call this function.
 
201
        """
 
202
        raise NotImplementedError(
 
203
                "No code should ever call this method for any reason")
 
204
 
 
205
    def getCredentialsCheckers(self):
 
206
        return []
 
207
 
 
208
 
 
209
class FileMessage:
 
210
    """A file we can write an email too."""
 
211
 
 
212
    implements(smtp.IMessage)
 
213
 
 
214
    def __init__(self, fp, name, finalName):
 
215
        self.fp = fp
 
216
        self.name = name
 
217
        self.finalName = finalName
 
218
 
 
219
    def lineReceived(self, line):
 
220
        self.fp.write(line+'\n')
 
221
 
 
222
    def eomReceived(self):
 
223
        self.fp.close()
 
224
        os.rename(self.name, self.finalName)
 
225
        return defer.succeed(self.finalName)
 
226
 
 
227
    def connectionLost(self):
 
228
        self.fp.close()
 
229
        os.remove(self.name)
 
230
 
 
231
 
 
232
class MailService(service.MultiService):
 
233
    """An email service."""
 
234
 
 
235
    queue = None
 
236
    domains = None
 
237
    portals = None
 
238
    aliases = None
 
239
    smtpPortal = None
 
240
 
 
241
    def __init__(self):
 
242
        service.MultiService.__init__(self)
 
243
        # Domains and portals for "client" protocols - POP3, IMAP4, etc
 
244
        self.domains = DomainWithDefaultDict({}, BounceDomain())
 
245
        self.portals = {}
 
246
 
 
247
        self.monitor = FileMonitoringService()
 
248
        self.monitor.setServiceParent(self)
 
249
        self.smtpPortal = cred.portal.Portal(self)
 
250
 
 
251
    def getPOP3Factory(self):
 
252
        return protocols.POP3Factory(self)
 
253
 
 
254
    def getSMTPFactory(self):
 
255
        return protocols.SMTPFactory(self, self.smtpPortal)
 
256
 
 
257
    def getESMTPFactory(self):
 
258
        return protocols.ESMTPFactory(self, self.smtpPortal)
 
259
 
 
260
    def addDomain(self, name, domain):
 
261
        portal = cred.portal.Portal(domain)
 
262
        map(portal.registerChecker, domain.getCredentialsCheckers())
 
263
        self.domains[name] = domain
 
264
        self.portals[name] = portal
 
265
        if self.aliases and IAliasableDomain.providedBy(domain):
 
266
            domain.setAliasGroup(self.aliases)
 
267
 
 
268
    def setQueue(self, queue):
 
269
        """Set the queue for outgoing emails."""
 
270
        self.queue = queue
 
271
 
 
272
    def requestAvatar(self, avatarId, mind, *interfaces):
 
273
        if smtp.IMessageDelivery in interfaces:
 
274
            a = protocols.ESMTPDomainDelivery(self, avatarId)
 
275
            return smtp.IMessageDelivery, a, lambda: None
 
276
        raise NotImplementedError()
 
277
 
 
278
    def lookupPortal(self, name):
 
279
        return self.portals[name]
 
280
 
 
281
    def defaultPortal(self):
 
282
        return self.portals['']
 
283
 
 
284
 
 
285
class FileMonitoringService(internet.TimerService):
 
286
 
 
287
    def __init__(self):
 
288
        self.files = []
 
289
        self.intervals = iter(util.IntervalDifferential([], 60))
 
290
 
 
291
    def startService(self):
 
292
        service.Service.startService(self)
 
293
        self._setupMonitor()
 
294
 
 
295
    def _setupMonitor(self):
 
296
        from twisted.internet import reactor
 
297
        t, self.index = self.intervals.next()
 
298
        self._call = reactor.callLater(t, self._monitor)
 
299
 
 
300
    def stopService(self):
 
301
        service.Service.stopService(self)
 
302
        if self._call:
 
303
            self._call.cancel()
 
304
            self._call = None
 
305
 
 
306
    def monitorFile(self, name, callback, interval=10):
 
307
        try:
 
308
            mtime = os.path.getmtime(name)
 
309
        except:
 
310
            mtime = 0
 
311
        self.files.append([interval, name, callback, mtime])
 
312
        self.intervals.addInterval(interval)
 
313
 
 
314
    def unmonitorFile(self, name):
 
315
        for i in range(len(self.files)):
 
316
            if name == self.files[i][1]:
 
317
                self.intervals.removeInterval(self.files[i][0])
 
318
                del self.files[i]
 
319
                break
 
320
 
 
321
    def _monitor(self):
 
322
        self._call = None
 
323
        if self.index is not None:
 
324
            name, callback, mtime = self.files[self.index][1:]
 
325
            try:
 
326
                now = os.path.getmtime(name)
 
327
            except:
 
328
                now = 0
 
329
            if now > mtime:
 
330
                log.msg("%s changed, notifying listener" % (name,))
 
331
                self.files[self.index][3] = now
 
332
                callback(name)
 
333
        self._setupMonitor()