|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- # -*- 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)
|