Viewing file: relay.py (5.14 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- test-case-name: twisted.mail.test.test_mail -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" Support for relaying mail. """
import os import pickle
from twisted.internet.address import UNIXAddress from twisted.mail import smtp from twisted.python import log
class DomainQueuer: """ An SMTP domain which add messages to a queue intended for relaying. """
def __init__(self, service, authenticated=False): self.service = service self.authed = authenticated
def exists(self, user): """ Check whether mail can be relayed to a user.
@type user: L{User} @param user: A user.
@rtype: no-argument callable which returns L{IMessage <smtp.IMessage>} provider @return: A function which takes no arguments and returns a message receiver for the user.
@raise SMTPBadRcpt: When mail cannot be relayed to the user. """ if self.willRelay(user.dest, user.protocol): # The most cursor form of verification of the addresses orig = filter(None, str(user.orig).split("@", 1)) dest = filter(None, str(user.dest).split("@", 1)) if len(orig) == 2 and len(dest) == 2: return lambda: self.startMessage(user) raise smtp.SMTPBadRcpt(user)
def willRelay(self, address, protocol): """ Check whether we agree to relay.
The default is to relay for all connections over UNIX sockets and all connections from localhost. """ peer = protocol.transport.getPeer() return self.authed or isinstance(peer, UNIXAddress) or peer.host == "127.0.0.1"
def startMessage(self, user): """ Create an envelope and a message receiver for the relay queue.
@type user: L{User} @param user: A user.
@rtype: L{IMessage <smtp.IMessage>} @return: A message receiver. """ queue = self.service.queue envelopeFile, smtpMessage = queue.createNewMessage() with envelopeFile: log.msg(f"Queueing mail {str(user.orig)!r} -> {str(user.dest)!r}") pickle.dump([str(user.orig), str(user.dest)], envelopeFile) return smtpMessage
class RelayerMixin:
# XXX - This is -totally- bogus # It opens about a -hundred- -billion- files # and -leaves- them open!
def loadMessages(self, messagePaths): self.messages = [] self.names = [] for message in messagePaths: with open(message + "-H", "rb") as fp: messageContents = pickle.load(fp) fp = open(message + "-D") messageContents.append(fp) self.messages.append(messageContents) self.names.append(message)
def getMailFrom(self): if not self.messages: return None return self.messages[0][0]
def getMailTo(self): if not self.messages: return None return [self.messages[0][1]]
def getMailData(self): if not self.messages: return None return self.messages[0][2]
def sentMail(self, code, resp, numOk, addresses, log): """Since we only use one recipient per envelope, this will be called with 0 or 1 addresses. We probably want to do something with the error message if we failed. """ if code in smtp.SUCCESS: # At least one, i.e. all, recipients successfully delivered os.remove(self.names[0] + "-D") os.remove(self.names[0] + "-H") del self.messages[0] del self.names[0]
class SMTPRelayer(RelayerMixin, smtp.SMTPClient): """ A base class for SMTP relayers. """
def __init__(self, messagePaths, *args, **kw): """ @type messagePaths: L{list} of L{bytes} @param messagePaths: The base filename for each message to be relayed.
@type args: 1-L{tuple} of (0) L{bytes} or 2-L{tuple} of (0) L{bytes}, (1) L{int} @param args: Positional arguments for L{SMTPClient.__init__}
@type kw: L{dict} @param kw: Keyword arguments for L{SMTPClient.__init__} """ smtp.SMTPClient.__init__(self, *args, **kw) self.loadMessages(messagePaths)
class ESMTPRelayer(RelayerMixin, smtp.ESMTPClient): """ A base class for ESMTP relayers. """
def __init__(self, messagePaths, *args, **kw): """ @type messagePaths: L{list} of L{bytes} @param messagePaths: The base filename for each message to be relayed.
@type args: 3-L{tuple} of (0) L{bytes}, (1) L{None} or L{ClientContextFactory <twisted.internet.ssl.ClientContextFactory>}, (2) L{bytes} or 4-L{tuple} of (0) L{bytes}, (1) L{None} or L{ClientContextFactory <twisted.internet.ssl.ClientContextFactory>}, (2) L{bytes}, (3) L{int} @param args: Positional arguments for L{ESMTPClient.__init__}
@type kw: L{dict} @param kw: Keyword arguments for L{ESMTPClient.__init__} """ smtp.ESMTPClient.__init__(self, *args, **kw) self.loadMessages(messagePaths)
|