Project:
Mailvisa
Code Location:
git://repo.or.cz/mailvisa.gitmaster
/
Outline
mailvisad.rb
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
require 'socket' require 'wordlist' require 'tokenize' ### Defaults confdir = ENV['HOME'] + '/settings/mailvisa' sockpath = 'mailvisad.sock' scorefile = 'scores' pidfile = 'mailvisad.pid' logfile = 'mailvisad.log' ### Functions def get_score word score = $scores[word] if score == nil 0.4 else score end end def most_interesting words ## Sort words by extremity interesting = words.sort do |x, y| (0.5 - get_score(y)).abs <=> (0.5 - get_score(x)).abs end # Return the most interesting words interesting[0,20] end def spam_probability scores prod = 1 scores.each { |x| prod = prod * x } div = 1 scores.each { |x| div = div * (1 - x) } prod / (prod + div) end usage = 'USAGE: ' + $0 + ' [options]' help = <<EOT Valid options are: -c <path> Look for files in <path> (default: $HOME/settings/mailvisa) -f <path> Load scores from <path> (default: scores) -l <path> Log to <path> (default: mailvisad.log) -p <path> Use <path> as pidfile (default: mailvisad.pid) -s <path> Use <path> as socket (default: mailvisad.sock) EOT ### Main program ## Process command line i = 0 while i < ARGV.length case ARGV[i] when '-h' puts usage print "\n" + help exit when '-c' i = i + 1 confdir = ARGV[i] when '-s' i = i + 1 sockpath = ARGV[i] when '-f' i = i + 1 scorefile = ARGV[i] when '-l' i = i + 1 logfile = ARGV[i] when '-p' i = i + 1 pidfile = ARGV[i] else $stderr.puts 'Invalid option: ' + ARGV[i] $stderr.puts usage $stderr.puts 'Use "' + $0 + ' -h" for help' exit 0x80 end i = i + 1 end sockpath = confdir + '/' + sockpath if sockpath.index('/') == nil scorefile = confdir + '/' + scorefile if scorefile.index('/') == nil pidfile = confdir + '/' + pidfile if pidfile.index('/') == nil logfile = confdir + '/' + logfile if logfile.index('/') == nil ## Define procedure to load configuration load_config = lambda do db = load_wordlist open(scorefile) $scores = db[:words] end # Set SIGHUP handler trap('SIGHUP') { load_config.call } # Open scorefile scorefh = open scorefile ## Open socket begin sock = UNIXServer.open sockpath rescue Errno::EADDRINUSE ## Figure out if the socket is actually live begin sock = UNIXSocket.new sockpath # Aye, inform user and exit $stderr.puts 'mailvisad already bound to ' + sockpath exit rescue # Nope, remove and try again if File.exists?(sockpath) && File.ftype(sockpath) == 'socket' File.unlink sockpath sock = UNIXServer.open sockpath end end end # Open logfile $stderr = open logfile, 'a' $stdout = $stderr # Fork child, exit parent exit if fork # Fork off a child, exit parent # Set at_exit handler at_exit { sock.close File.unlink sockpath if File.exists?(sockpath) && File.ftype(sockpath) == 'socket' } ## Detach from terminal $stdin.close pid = Process.setsid ## Create pidfile fh = open pidfile, 'w' fh.puts pid fh.close # Load words $scores = load_wordlist(scorefh)[:words] while true conn = sock.accept begin message = conn.read 16384 words = most_interesting tokenize(message) scores = words.map { |x| get_score x } probability = (spam_probability(scores) * 100).to_i / 100.0 conn.puts probability rescue ensure conn.close end end
