1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#!/usr/bin/env python
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Example of an interface to Courier's mail filter.
"""
LOGFILE = "/tmp/filter.log"
# Setup log file
from twisted.python import log
log.startLogging(open(LOGFILE, "a"))
import sys
sys.stderr = log.logfile
# Twisted imports
from twisted.internet import reactor, stdio
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols import basic
FILTERS = "/var/lib/courier/filters"
ALLFILTERS = "/var/lib/courier/allfilters"
FILTERNAME = "twistedfilter"
import email.message
import email.parser
import os
import os.path
from syslog import LOG_MAIL, openlog, syslog
def trace_dump():
t, v, tb = sys.exc_info()
openlog(FILTERNAME, 0, LOG_MAIL)
syslog(f"Unhandled exception: {v} - {t}")
while tb:
syslog(
"Trace: {}:{} {}".format(
tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_name, tb.tb_lineno
)
)
tb = tb.tb_next
# just to be safe
del tb
def safe_del(file):
try:
if os.path.isdir(file):
os.removedirs(file)
else:
os.remove(file)
except OSError:
pass
class DieWhenLost(Protocol):
def connectionLost(self, reason=None):
reactor.stop()
class MailProcessor(basic.LineReceiver):
"""
I process a mail message.
Override filterMessage to do any filtering you want.
"""
messageFilename = None
delimiter = "\n"
def connectionMade(self):
log.msg(f"Connection from {self.transport}")
self.state = "connected"
self.metaInfo = []
def lineReceived(self, line):
if self.state == "connected":
self.messageFilename = line
self.state = "gotMessageFilename"
if self.state == "gotMessageFilename":
if line:
self.metaInfo.append(line)
else:
if not self.metaInfo:
self.transport.loseConnection()
return
self.filterMessage()
def filterMessage(self):
"""Override this.
A trivial example is included.
"""
try:
emailParser = email.parser.Parser()
with open(self.messageFilename) as f:
emailParser.parse(f)
self.sendLine(b"200 Ok")
except BaseException:
trace_dump()
self.sendLine(b"435 " + FILTERNAME.encode("ascii") + b" processing error")
def main():
# Listen on the UNIX socket
f = Factory()
f.protocol = MailProcessor
safe_del(f"{ALLFILTERS}/{FILTERNAME}")
reactor.listenUNIX(f"{ALLFILTERS}/{FILTERNAME}", f, 10)
# Once started, close fd 3 to let Courier know we're ready
reactor.callLater(0, os.close, 3)
# When stdin is closed, it's time to exit.
stdio.StandardIO(DieWhenLost())
# Go!
reactor.run()
if __name__ == "__main__":
main()
|