annotate spamc-milter.py @ 0:7f4443127958 default tip

Import
author Daniele Nicolodi <daniele@grinta.net>
date Wed, 02 Nov 2011 12:03:09 +0100
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
1 # Simple milter deamon that handles email messages off to spamd for
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
2 # classification. Messages classified as spam are rejected. Other
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
3 # are augmented with Spamassassin additional headers.
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
4
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
5 import email.parser
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
6 from cStringIO import StringIO
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
7
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
8 import Milter as milter
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
9 import spamc
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
10
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
11
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
12 # headers to add to scanned messages
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
13 HEADERS = ('X-Spam-Checker-Version', 'X-Spam-Level', 'X-Spam-Status', )
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
14
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
15
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
16 class Milter(milter.Base):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
17
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
18 def __init__(self):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
19 # integer incremented with each call
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
20 self.id = milter.uniqueID()
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
21 self.fd = StringIO()
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
22
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
23 @milter.noreply
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
24 def connect(self, hostname, family, hostaddr):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
25 self.hostname = hostname
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
26 self.hostaddr = hostaddr
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
27 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
28
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
29 def hello(self, heloname):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
30 self.helo = heloname
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
31 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
32
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
33 def envfrom(self, mailfrom, *str):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
34 self.mailfrom = mailfrom
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
35 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
36
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
37 @milter.noreply
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
38 def envrcpt(self, recipient, *str):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
39 self.recipient = recipient
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
40 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
41
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
42 @milter.noreply
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
43 def header(self, name, val):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
44 # add header to buffer
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
45 self.fd.write('%s: %s\n' % (name, val))
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
46 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
47
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
48 @milter.noreply
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
49 def eoh(self):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
50 # terminate headers
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
51 self.fd.write('\n')
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
52 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
53
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
54 @milter.noreply
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
55 def body(self, chunk):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
56 # add body to buffer
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
57 self.fd.write(chunk)
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
58 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
59
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
60 def eom(self):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
61 # ask spamd about the received message
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
62 self.conn = spamc.Client('localhost')
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
63 resp = self.conn.request('HEADERS', self.fd.getvalue())
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
64 # reject spam messages
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
65 if resp.result:
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
66 return milter.REJECT
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
67 # parse returned message headers
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
68 data = resp.read()
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
69 print data
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
70 headers = email.parser.Parser().parsestr(data)
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
71 # add headers
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
72 for name in HEADERS:
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
73 # transform multiple line headers to single line ones
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
74 value = ' '.join(headers.get(name, '').split())
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
75 self.addheader(name, value)
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
76 # accept message
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
77 return milter.ACCEPT
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
78
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
79 def close(self):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
80 # clean up
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
81 self.fd.close()
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
82 self.conn.close()
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
83 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
84
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
85 def abort(self):
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
86 # client disconnected prematurely
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
87 return milter.CONTINUE
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
88
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
89
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
90 def main():
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
91 socketname = "/var/spool/postfix/private/spamc"
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
92 timeout = 600
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
93
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
94 # register the milter factory
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
95 milter.factory = Milter
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
96 # ask milter features we use
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
97 milter.set_flags(milter.CHGHDRS + milter.ADDHDRS)
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
98 # run
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
99 milter.runmilter("spamc-milter", socketname, timeout)
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
100
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
101
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
102 if __name__ == "__main__":
Daniele Nicolodi <daniele@grinta.net>
parents:
diff changeset
103 main()