| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
"""Support for relaying mail for twisted.mail""" |
|---|
| 7 |
|
|---|
| 8 |
from twisted.mail import smtp |
|---|
| 9 |
from twisted.python import log |
|---|
| 10 |
from twisted.internet.address import UNIXAddress |
|---|
| 11 |
|
|---|
| 12 |
import os |
|---|
| 13 |
|
|---|
| 14 |
try: |
|---|
| 15 |
import cPickle as pickle |
|---|
| 16 |
except ImportError: |
|---|
| 17 |
import pickle |
|---|
| 18 |
|
|---|
| 19 |
class DomainQueuer: |
|---|
| 20 |
"""An SMTP domain which add messages to a queue intended for relaying.""" |
|---|
| 21 |
|
|---|
| 22 |
def __init__(self, service, authenticated=False): |
|---|
| 23 |
self.service = service |
|---|
| 24 |
self.authed = authenticated |
|---|
| 25 |
|
|---|
| 26 |
def exists(self, user): |
|---|
| 27 |
"""Check whether we will relay |
|---|
| 28 |
|
|---|
| 29 |
Call overridable willRelay method |
|---|
| 30 |
""" |
|---|
| 31 |
if self.willRelay(user.dest, user.protocol): |
|---|
| 32 |
|
|---|
| 33 |
orig = filter(None, str(user.orig).split('@', 1)) |
|---|
| 34 |
dest = filter(None, str(user.dest).split('@', 1)) |
|---|
| 35 |
if len(orig) == 2 and len(dest) == 2: |
|---|
| 36 |
return lambda: self.startMessage(user) |
|---|
| 37 |
raise smtp.SMTPBadRcpt(user) |
|---|
| 38 |
|
|---|
| 39 |
def willRelay(self, address, protocol): |
|---|
| 40 |
"""Check whether we agree to relay |
|---|
| 41 |
|
|---|
| 42 |
The default is to relay for all connections over UNIX |
|---|
| 43 |
sockets and all connections from localhost. |
|---|
| 44 |
""" |
|---|
| 45 |
peer = protocol.transport.getPeer() |
|---|
| 46 |
return self.authed or isinstance(peer, UNIXAddress) or peer.host == '127.0.0.1' |
|---|
| 47 |
|
|---|
| 48 |
def startMessage(self, user): |
|---|
| 49 |
"""Add envelope to queue and returns ISMTPMessage.""" |
|---|
| 50 |
queue = self.service.queue |
|---|
| 51 |
envelopeFile, smtpMessage = queue.createNewMessage() |
|---|
| 52 |
try: |
|---|
| 53 |
log.msg('Queueing mail %r -> %r' % (str(user.orig), str(user.dest))) |
|---|
| 54 |
pickle.dump([str(user.orig), str(user.dest)], envelopeFile) |
|---|
| 55 |
finally: |
|---|
| 56 |
envelopeFile.close() |
|---|
| 57 |
return smtpMessage |
|---|
| 58 |
|
|---|
| 59 |
class RelayerMixin: |
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
def loadMessages(self, messagePaths): |
|---|
| 66 |
self.messages = [] |
|---|
| 67 |
self.names = [] |
|---|
| 68 |
for message in messagePaths: |
|---|
| 69 |
fp = open(message+'-H') |
|---|
| 70 |
try: |
|---|
| 71 |
messageContents = pickle.load(fp) |
|---|
| 72 |
finally: |
|---|
| 73 |
fp.close() |
|---|
| 74 |
fp = open(message+'-D') |
|---|
| 75 |
messageContents.append(fp) |
|---|
| 76 |
self.messages.append(messageContents) |
|---|
| 77 |
self.names.append(message) |
|---|
| 78 |
|
|---|
| 79 |
def getMailFrom(self): |
|---|
| 80 |
if not self.messages: |
|---|
| 81 |
return None |
|---|
| 82 |
return self.messages[0][0] |
|---|
| 83 |
|
|---|
| 84 |
def getMailTo(self): |
|---|
| 85 |
if not self.messages: |
|---|
| 86 |
return None |
|---|
| 87 |
return [self.messages[0][1]] |
|---|
| 88 |
|
|---|
| 89 |
def getMailData(self): |
|---|
| 90 |
if not self.messages: |
|---|
| 91 |
return None |
|---|
| 92 |
return self.messages[0][2] |
|---|
| 93 |
|
|---|
| 94 |
def sentMail(self, code, resp, numOk, addresses, log): |
|---|
| 95 |
"""Since we only use one recipient per envelope, this |
|---|
| 96 |
will be called with 0 or 1 addresses. We probably want |
|---|
| 97 |
to do something with the error message if we failed. |
|---|
| 98 |
""" |
|---|
| 99 |
if code in smtp.SUCCESS: |
|---|
| 100 |
|
|---|
| 101 |
os.remove(self.names[0]+'-D') |
|---|
| 102 |
os.remove(self.names[0]+'-H') |
|---|
| 103 |
del self.messages[0] |
|---|
| 104 |
del self.names[0] |
|---|
| 105 |
|
|---|
| 106 |
class SMTPRelayer(RelayerMixin, smtp.SMTPClient): |
|---|
| 107 |
def __init__(self, messagePaths, *args, **kw): |
|---|
| 108 |
smtp.SMTPClient.__init__(self, *args, **kw) |
|---|
| 109 |
self.loadMessages(messagePaths) |
|---|
| 110 |
|
|---|
| 111 |
class ESMTPRelayer(RelayerMixin, smtp.ESMTPClient): |
|---|
| 112 |
def __init__(self, messagePaths, *args, **kw): |
|---|
| 113 |
smtp.ESMTPClient.__init__(self, *args, **kw) |
|---|
| 114 |
self.loadMessages(messagePaths) |
|---|