123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- # -*- test-case-name: twisted.test.test_postfix -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Postfix mail transport agent related protocols.
- """
- import sys
- try:
- # Python 2
- from UserDict import UserDict
- except ImportError:
- # Python 3
- from collections import UserDict
- try:
- # Python 2
- from urllib import quote as _quote, unquote as _unquote
- except ImportError:
- # Python 3
- from urllib.parse import quote as _quote, unquote as _unquote
- from twisted.protocols import basic
- from twisted.protocols import policies
- from twisted.internet import protocol, defer
- from twisted.python import log
- from twisted.python.compat import unicode
- # urllib's quote functions just happen to match
- # the postfix semantics.
- def quote(s):
- quoted = _quote(s)
- if isinstance(quoted, unicode):
- quoted = quoted.encode("ascii")
- return quoted
- def unquote(s):
- if isinstance(s, bytes):
- s = s.decode("ascii")
- quoted = _unquote(s)
- return quoted.encode("ascii")
- class PostfixTCPMapServer(basic.LineReceiver, policies.TimeoutMixin):
- """
- Postfix mail transport agent TCP map protocol implementation.
- Receive requests for data matching given key via lineReceived,
- asks it's factory for the data with self.factory.get(key), and
- returns the data to the requester. None means no entry found.
- You can use postfix's postmap to test the map service::
- /usr/sbin/postmap -q KEY tcp:localhost:4242
- """
- timeout = 600
- delimiter = b'\n'
- def connectionMade(self):
- self.setTimeout(self.timeout)
- def sendCode(self, code, message=b''):
- """
- Send an SMTP-like code with a message.
- """
- self.sendLine(str(code).encode("ascii") + b' ' + message)
- def lineReceived(self, line):
- self.resetTimeout()
- try:
- request, params = line.split(None, 1)
- except ValueError:
- request = line
- params = None
- try:
- f = getattr(self, u'do_' + request.decode("ascii"))
- except AttributeError:
- self.sendCode(400, b'unknown command')
- else:
- try:
- f(params)
- except:
- excInfo = str(sys.exc_info()[1]).encode("ascii")
- self.sendCode(400, b'Command ' + request + b' failed: ' +
- excInfo)
- def do_get(self, key):
- if key is None:
- self.sendCode(400, b"Command 'get' takes 1 parameters.")
- else:
- d = defer.maybeDeferred(self.factory.get, key)
- d.addCallbacks(self._cbGot, self._cbNot)
- d.addErrback(log.err)
- def _cbNot(self, fail):
- msg = fail.getErrorMessage().encode("ascii")
- self.sendCode(400, msg)
- def _cbGot(self, value):
- if value is None:
- self.sendCode(500)
- else:
- self.sendCode(200, quote(value))
- def do_put(self, keyAndValue):
- if keyAndValue is None:
- self.sendCode(400, b"Command 'put' takes 2 parameters.")
- else:
- try:
- key, value = keyAndValue.split(None, 1)
- except ValueError:
- self.sendCode(400, b"Command 'put' takes 2 parameters.")
- else:
- self.sendCode(500, b'put is not implemented yet.')
- class PostfixTCPMapDictServerFactory(UserDict, protocol.ServerFactory):
- """
- An in-memory dictionary factory for PostfixTCPMapServer.
- """
- protocol = PostfixTCPMapServer
- class PostfixTCPMapDeferringDictServerFactory(protocol.ServerFactory):
- """
- An in-memory dictionary factory for PostfixTCPMapServer.
- """
- protocol = PostfixTCPMapServer
- def __init__(self, data=None):
- self.data = {}
- if data is not None:
- self.data.update(data)
- def get(self, key):
- return defer.succeed(self.data.get(key))
|