blob: aae4932e1a7c61d1dd73dc7e2df75118c8d5d6ab (
plain)
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
|
#!/usr/bin/ruby
require 'rmail'
if ARGV[0] == "spam"
MAILIN="mail/spam-in"
MAILLEARN="mail/spam-learn"
OUTBOX="mail/outbox"
SEND_DISCARD_MAIL = true
elsif ARGV[0] == "ham"
MAILIN="mail/ham-in"
MAILLEARN="mail/ham-learn"
OUTBOX="mail/outbox"
SEND_DISCARD_MAIL = false
else
STDERR.puts "Usage: #{$0} ham|spam"
exit 1
end
DOMAIN='lists.oftc.net'
FROM="listmod@blackhole.oftc.net"
HOSTNAME=`hostname`.chop
class Counter
@@counter = 0
def Counter.value
@@counter += 1
end
end
def uniqueName
"%d.%d_%d.%s"%[
Time.now.to_i,
$$,
Counter.value,
HOSTNAME]
end
def check_maildir(d)
throw "#{d} is not a maildir" unless FileTest.directory?(d)
throw "#{d} is not a maildir" unless FileTest.directory?(d+"/new")
throw "#{d} is not a maildir" unless FileTest.directory?(d+"/cur")
throw "#{d} is not a maildir" unless FileTest.directory?(d+"/tmp")
true
end
def store_in_maildir(md, msg)
fn = uniqueName
File.open(md+"/tmp/"+fn, "w", 0600) do |f|
f.puts msg
end
File.link(md+"/tmp/"+fn, md+"/new/"+fn)
File.unlink(md+"/tmp/"+fn)
md+"/new/"+fn
end
def process_mail(filename)
message = File.open(filename) { |f| RMail::Parser.read(f) }
# some sanity checks
throw "Mailman moderation mails are expected to have 3 mime parts" unless message.body.length == 3
throw "Mime Part 0 does have an unexpected content type: #{message.body[0].header['Content-Type']}" unless message.body[0].header['Content-Type'] == 'text/plain; charset="us-ascii"'
throw "Mime Part 1 does have an unexpected content type: #{message.body[1].header['Content-Type']}" unless message.body[1].header['Content-Type'] == 'message/rfc822'
throw "Mime Part 2 does have an unexpected content type: #{message.body[2].header['Content-Type']}" unless message.body[2].header['Content-Type'] == 'message/rfc822'
explanation_body = message.body[0].body
held_part = RMail::Parser.read( message.body[1].body )
discard_part = RMail::Parser.read( message.body[2].body )
# more sanity checks
throw "Did not find 'As list administrator, your..' boilerplate in mail" unless explanation_body =~ /^As list administrator, your authorization is requested for the/
throw "Did not find listname in mail" unless explanation_body =~ /^ *List: *.*@#{DOMAIN}/
throw "discard_part does have an unexpected content type: #{discard_part.header['Content-Type']}" unless discard_part.header['Content-Type'] == 'text/plain; charset="us-ascii"'
request_address = discard_part.header['From']
throw "discard_part does not have a from address" unless request_address
matchdata = /confirm ([0-9a-f]*)/.match discard_part.header['Subject']
throw "Could not find cookie in discard_part" unless matchdata and matchdata[1]
cookie = matchdata[1]
store_in_maildir(MAILLEARN, held_part)
if SEND_DISCARD_MAIL
mail_request = RMail::Message.new()
mail_request.header['From'] = FROM
mail_request.header['To'] = request_address
mail_request.header['Subject'] = "Re: confirm #{cookie}"
mail_request.body = ''
store_in_maildir(OUTBOX, mail_request)
end
end
check_maildir MAILIN
check_maildir MAILLEARN
check_maildir OUTBOX
Dir[MAILIN+"/new/*"].each do |filename|
begin
process_mail filename
File.unlink filename
rescue Exception => e
STDERR.puts "Error when processing #{filename}: #{e}"
end
end
|