Mini Shell
# -*- 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)
Zerion Mini Shell 1.0