Index: third_party/twisted_8_1/twisted/mail/alias.py |
diff --git a/third_party/twisted_8_1/twisted/mail/alias.py b/third_party/twisted_8_1/twisted/mail/alias.py |
deleted file mode 100644 |
index 4005c74bd1c0d916d0f1849f8f55e774e18390d2..0000000000000000000000000000000000000000 |
--- a/third_party/twisted_8_1/twisted/mail/alias.py |
+++ /dev/null |
@@ -1,435 +0,0 @@ |
-# -*- test-case-name: twisted.mail.test.test_mail -*- |
-# |
-# Copyright (c) 2001-2007 Twisted Matrix Laboratories. |
-# See LICENSE for details. |
- |
- |
-""" |
-Support for aliases(5) configuration files |
- |
-@author: U{Jp Calderone<mailto:exarkun@twistedmatrix.com>} |
- |
-TODO:: |
- Monitor files for reparsing |
- Handle non-local alias targets |
- Handle maildir alias targets |
-""" |
- |
-import os |
-import tempfile |
- |
-from twisted.mail import smtp |
-from twisted.internet import reactor |
-from twisted.internet import protocol |
-from twisted.internet import defer |
-from twisted.python import failure |
-from twisted.python import log |
-from zope.interface import implements, Interface |
- |
- |
-def handle(result, line, filename, lineNo): |
- parts = [p.strip() for p in line.split(':', 1)] |
- if len(parts) != 2: |
- fmt = "Invalid format on line %d of alias file %s." |
- arg = (lineNo, filename) |
- log.err(fmt % arg) |
- else: |
- user, alias = parts |
- result.setdefault(user.strip(), []).extend(map(str.strip, alias.split(','))) |
- |
-def loadAliasFile(domains, filename=None, fp=None): |
- """Load a file containing email aliases. |
- |
- Lines in the file should be formatted like so:: |
- |
- username: alias1,alias2,...,aliasN |
- |
- Aliases beginning with a | will be treated as programs, will be run, and |
- the message will be written to their stdin. |
- |
- Aliases without a host part will be assumed to be addresses on localhost. |
- |
- If a username is specified multiple times, the aliases for each are joined |
- together as if they had all been on one line. |
- |
- @type domains: C{dict} of implementor of C{IDomain} |
- @param domains: The domains to which these aliases will belong. |
- |
- @type filename: C{str} |
- @param filename: The filename from which to load aliases. |
- |
- @type fp: Any file-like object. |
- @param fp: If specified, overrides C{filename}, and aliases are read from |
- it. |
- |
- @rtype: C{dict} |
- @return: A dictionary mapping usernames to C{AliasGroup} objects. |
- """ |
- result = {} |
- if fp is None: |
- fp = file(filename) |
- else: |
- filename = getattr(fp, 'name', '<unknown>') |
- i = 0 |
- prev = '' |
- for line in fp: |
- i += 1 |
- line = line.rstrip() |
- if line.lstrip().startswith('#'): |
- continue |
- elif line.startswith(' ') or line.startswith('\t'): |
- prev = prev + line |
- else: |
- if prev: |
- handle(result, prev, filename, i) |
- prev = line |
- if prev: |
- handle(result, prev, filename, i) |
- for (u, a) in result.items(): |
- addr = smtp.Address(u) |
- result[u] = AliasGroup(a, domains, u) |
- return result |
- |
-class IAlias(Interface): |
- def createMessageReceiver(): |
- pass |
- |
-class AliasBase: |
- def __init__(self, domains, original): |
- self.domains = domains |
- self.original = smtp.Address(original) |
- |
- def domain(self): |
- return self.domains[self.original.domain] |
- |
- def resolve(self, aliasmap, memo=None): |
- if memo is None: |
- memo = {} |
- if str(self) in memo: |
- return None |
- memo[str(self)] = None |
- return self.createMessageReceiver() |
- |
-class AddressAlias(AliasBase): |
- """The simplest alias, translating one email address into another.""" |
- |
- implements(IAlias) |
- |
- def __init__(self, alias, *args): |
- AliasBase.__init__(self, *args) |
- self.alias = smtp.Address(alias) |
- |
- def __str__(self): |
- return '<Address %s>' % (self.alias,) |
- |
- def createMessageReceiver(self): |
- return self.domain().startMessage(str(self.alias)) |
- |
- def resolve(self, aliasmap, memo=None): |
- if memo is None: |
- memo = {} |
- if str(self) in memo: |
- return None |
- memo[str(self)] = None |
- try: |
- return self.domain().exists(smtp.User(self.alias, None, None, None), memo)() |
- except smtp.SMTPBadRcpt: |
- pass |
- if self.alias.local in aliasmap: |
- return aliasmap[self.alias.local].resolve(aliasmap, memo) |
- return None |
- |
-class FileWrapper: |
- implements(smtp.IMessage) |
- |
- def __init__(self, filename): |
- self.fp = tempfile.TemporaryFile() |
- self.finalname = filename |
- |
- def lineReceived(self, line): |
- self.fp.write(line + '\n') |
- |
- def eomReceived(self): |
- self.fp.seek(0, 0) |
- try: |
- f = file(self.finalname, 'a') |
- except: |
- return defer.fail(failure.Failure()) |
- |
- f.write(self.fp.read()) |
- self.fp.close() |
- f.close() |
- |
- return defer.succeed(self.finalname) |
- |
- def connectionLost(self): |
- self.fp.close() |
- self.fp = None |
- |
- def __str__(self): |
- return '<FileWrapper %s>' % (self.finalname,) |
- |
- |
-class FileAlias(AliasBase): |
- |
- implements(IAlias) |
- |
- def __init__(self, filename, *args): |
- AliasBase.__init__(self, *args) |
- self.filename = filename |
- |
- def __str__(self): |
- return '<File %s>' % (self.filename,) |
- |
- def createMessageReceiver(self): |
- return FileWrapper(self.filename) |
- |
- |
- |
-class ProcessAliasTimeout(Exception): |
- """ |
- A timeout occurred while processing aliases. |
- """ |
- |
- |
- |
-class MessageWrapper: |
- """ |
- A message receiver which delivers content to a child process. |
- |
- @type completionTimeout: C{int} or C{float} |
- @ivar completionTimeout: The number of seconds to wait for the child |
- process to exit before reporting the delivery as a failure. |
- |
- @type _timeoutCallID: C{NoneType} or L{IDelayedCall} |
- @ivar _timeoutCallID: The call used to time out delivery, started when the |
- connection to the child process is closed. |
- |
- @type done: C{bool} |
- @ivar done: Flag indicating whether the child process has exited or not. |
- |
- @ivar reactor: An L{IReactorTime} provider which will be used to schedule |
- timeouts. |
- """ |
- implements(smtp.IMessage) |
- |
- done = False |
- |
- completionTimeout = 60 |
- _timeoutCallID = None |
- |
- reactor = reactor |
- |
- def __init__(self, protocol, process=None, reactor=None): |
- self.processName = process |
- self.protocol = protocol |
- self.completion = defer.Deferred() |
- self.protocol.onEnd = self.completion |
- self.completion.addBoth(self._processEnded) |
- |
- if reactor is not None: |
- self.reactor = reactor |
- |
- |
- def _processEnded(self, result): |
- """ |
- Record process termination and cancel the timeout call if it is active. |
- """ |
- self.done = True |
- if self._timeoutCallID is not None: |
- # eomReceived was called, we're actually waiting for the process to |
- # exit. |
- self._timeoutCallID.cancel() |
- self._timeoutCallID = None |
- else: |
- # eomReceived was not called, this is unexpected, propagate the |
- # error. |
- return result |
- |
- |
- def lineReceived(self, line): |
- if self.done: |
- return |
- self.protocol.transport.write(line + '\n') |
- |
- |
- def eomReceived(self): |
- """ |
- Disconnect from the child process, set up a timeout to wait for it to |
- exit, and return a Deferred which will be called back when the child |
- process exits. |
- """ |
- if not self.done: |
- self.protocol.transport.loseConnection() |
- self._timeoutCallID = self.reactor.callLater( |
- self.completionTimeout, self._completionCancel) |
- return self.completion |
- |
- |
- def _completionCancel(self): |
- """ |
- Handle the expiration of the timeout for the child process to exit by |
- terminating the child process forcefully and issuing a failure to the |
- completion deferred returned by L{eomReceived}. |
- """ |
- self._timeoutCallID = None |
- self.protocol.transport.signalProcess('KILL') |
- exc = ProcessAliasTimeout( |
- "No answer after %s seconds" % (self.completionTimeout,)) |
- self.protocol.onEnd = None |
- self.completion.errback(failure.Failure(exc)) |
- |
- |
- def connectionLost(self): |
- # Heh heh |
- pass |
- |
- |
- def __str__(self): |
- return '<ProcessWrapper %s>' % (self.processName,) |
- |
- |
- |
-class ProcessAliasProtocol(protocol.ProcessProtocol): |
- """ |
- Trivial process protocol which will callback a Deferred when the associated |
- process ends. |
- |
- @ivar onEnd: If not C{None}, a L{Deferred} which will be called back with |
- the failure passed to C{processEnded}, when C{processEnded} is called. |
- """ |
- |
- onEnd = None |
- |
- def processEnded(self, reason): |
- """ |
- Call back C{onEnd} if it is set. |
- """ |
- if self.onEnd is not None: |
- self.onEnd.errback(reason) |
- |
- |
- |
-class ProcessAlias(AliasBase): |
- """ |
- An alias which is handled by the execution of a particular program. |
- |
- @ivar reactor: An L{IReactorProcess} and L{IReactorTime} provider which |
- will be used to create and timeout the alias child process. |
- """ |
- implements(IAlias) |
- |
- reactor = reactor |
- |
- def __init__(self, path, *args): |
- AliasBase.__init__(self, *args) |
- self.path = path.split() |
- self.program = self.path[0] |
- |
- |
- def __str__(self): |
- """ |
- Build a string representation containing the path. |
- """ |
- return '<Process %s>' % (self.path,) |
- |
- |
- def spawnProcess(self, proto, program, path): |
- """ |
- Wrapper around C{reactor.spawnProcess}, to be customized for tests |
- purpose. |
- """ |
- return self.reactor.spawnProcess(proto, program, path) |
- |
- |
- def createMessageReceiver(self): |
- """ |
- Create a message receiver by launching a process. |
- """ |
- p = ProcessAliasProtocol() |
- m = MessageWrapper(p, self.program, self.reactor) |
- fd = self.spawnProcess(p, self.program, self.path) |
- return m |
- |
- |
- |
-class MultiWrapper: |
- """ |
- Wrapper to deliver a single message to multiple recipients. |
- """ |
- |
- implements(smtp.IMessage) |
- |
- def __init__(self, objs): |
- self.objs = objs |
- |
- def lineReceived(self, line): |
- for o in self.objs: |
- o.lineReceived(line) |
- |
- def eomReceived(self): |
- return defer.DeferredList([ |
- o.eomReceived() for o in self.objs |
- ]) |
- |
- def connectionLost(self): |
- for o in self.objs: |
- o.connectionLost() |
- |
- def __str__(self): |
- return '<GroupWrapper %r>' % (map(str, self.objs),) |
- |
- |
- |
-class AliasGroup(AliasBase): |
- """ |
- An alias which points to more than one recipient. |
- |
- @ivar processAliasFactory: a factory for resolving process aliases. |
- @type processAliasFactory: C{class} |
- """ |
- |
- implements(IAlias) |
- |
- processAliasFactory = ProcessAlias |
- |
- def __init__(self, items, *args): |
- AliasBase.__init__(self, *args) |
- self.aliases = [] |
- while items: |
- addr = items.pop().strip() |
- if addr.startswith(':'): |
- try: |
- f = file(addr[1:]) |
- except: |
- log.err("Invalid filename in alias file %r" % (addr[1:],)) |
- else: |
- addr = ' '.join([l.strip() for l in f]) |
- items.extend(addr.split(',')) |
- elif addr.startswith('|'): |
- self.aliases.append(self.processAliasFactory(addr[1:], *args)) |
- elif addr.startswith('/'): |
- if os.path.isdir(addr): |
- log.err("Directory delivery not supported") |
- else: |
- self.aliases.append(FileAlias(addr, *args)) |
- else: |
- self.aliases.append(AddressAlias(addr, *args)) |
- |
- def __len__(self): |
- return len(self.aliases) |
- |
- def __str__(self): |
- return '<AliasGroup [%s]>' % (', '.join(map(str, self.aliases))) |
- |
- def createMessageReceiver(self): |
- return MultiWrapper([a.createMessageReceiver() for a in self.aliases]) |
- |
- def resolve(self, aliasmap, memo=None): |
- if memo is None: |
- memo = {} |
- r = [] |
- for a in self.aliases: |
- r.append(a.resolve(aliasmap, memo)) |
- return MultiWrapper(filter(None, r)) |
- |